fix: logo polda and satker

This commit is contained in:
Sabda Yagra 2025-03-10 19:49:11 +07:00
commit 444ddd6fcb
89 changed files with 5096 additions and 3199 deletions

View File

@ -40,19 +40,19 @@ const columns: ColumnDef<any>[] = [
}, },
{ {
accessorKey: "name", accessorKey: "fullname",
header: "Nama", header: "Nama",
cell: ({ row }) => <span>{row.getValue("name")}</span>, cell: ({ row }) => <span>{row.getValue("fullname")}</span>,
}, },
{ {
accessorKey: "region", accessorKey: "address",
header: "Wilayah", header: "Wilayah",
cell: ({ row }) => <span>{row.getValue("region")}</span>, cell: ({ row }) => <span>{row.getValue("address")}</span>,
}, },
{ {
accessorKey: "skills", accessorKey: "role.name",
header: "Bidang Keahlian", header: "Bidang Keahlian",
cell: ({ row }) => <span>{row.getValue("skills")}</span>, cell: ({ row }) => <span>{row.original.role?.name ?? "-"}</span>,
}, },
{ {

View File

@ -53,6 +53,7 @@ import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal"; import { close, loading } from "@/config/swal";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import { listDataExperts } from "@/service/experts/experts";
const dummyData = [ const dummyData = [
{ {
@ -93,6 +94,7 @@ const AddExpertTable = () => {
const [statusFilter, setStatusFilter] = React.useState<number[]>([]); const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1); const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,
@ -145,19 +147,44 @@ const AddExpertTable = () => {
}); });
}, [page, showData]); }, [page, showData]);
async function fetchData() { // async function fetchData() {
try { // try {
loading(); // loading();
const contentData = dummyData; // const contentData = dummyData;
// contentData.forEach((item: any, index: number) => {
// item.no = (page - 1) * Number(showData) + index + 1;
// });
// setDataTable(contentData);
// setTotalData(contentData?.length);
// setTotalPage(1);
// close();
// } catch (error) {
// console.error("Error fetching tasks:", error);
// }
// }
async function fetchData() {
// const formattedStartDate = startDate
// ? format(new Date(startDate), "yyyy-MM-dd")
// : "";
// const formattedEndDate = endDate
// ? format(new Date(endDate), "yyyy-MM-dd")
// : "";
try {
// const isForSelf = Number(roleId) === 4;
const res = await listDataExperts(limit, page - 1);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => { contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * Number(showData) + index + 1; item.no = (page - 1) * limit + index + 1;
}); });
setDataTable(contentData); setDataTable(contentData);
setTotalData(contentData?.length); setTotalData(data?.totalElements);
setTotalPage(1); setTotalPage(data?.totalPages);
close();
} catch (error) { } catch (error) {
console.error("Error fetching tasks:", error); console.error("Error fetching tasks:", error);
} }

View File

@ -23,27 +23,30 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { useEffect, useState } from "react";
import { AdministrationLevelList, getListCompetencies, getListExperiences, saveUserInternal, saveUserRolePlacements } from "@/service/management-user/management-user";
import { loading } from "@/config/swal";
const FormSchema = z.object({ const FormSchema = z.object({
name: z.string({ name: z.string({
required_error: "Required", required_error: "Required",
}), }),
username: z.string({
required_error: "Required",
}),
password: z.string({
required_error: "Required",
}),
phoneNumber: z.string({ phoneNumber: z.string({
required_error: "Required", required_error: "Required",
}), }),
email: z.string({ email: z.string({
required_error: "Required", required_error: "Required",
}), }),
position: z.string({
required_error: "Required",
}),
region: z.string({
required_error: "Required",
}),
skills: z.string({ skills: z.string({
required_error: "Required", required_error: "Required",
}), }),
experience: z.string({ experiences: z.string({
required_error: "Required", required_error: "Required",
}), }),
company: z.string({ company: z.string({
@ -51,12 +54,34 @@ const FormSchema = z.object({
}), }),
}); });
export type Placements = {
index: number;
roleId?: string;
userLevelId?: number;
}
export default function AddExpertForm() { export default function AddExpertForm() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({ const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema), resolver: zodResolver(FormSchema),
}); });
const [incrementId, setIncrementId] = useState(1);
const [placementRows, setPlacementRows] = useState<Placements[]>([{ index: 0, roleId: "", userLevelId: 0 }]);
const [userCompetencies, setUserCompetencies] = useState<any>();
const [userExperiences, setUserExperiences] = useState<any>();
const [userLevels, setUserLevels] = useState<any>();
const roleSelection = [
{
id: "11",
name: "Koor Kurator",
},
{
id: "12",
name: "Kurator",
}
];
const onSubmit = async (data: z.infer<typeof FormSchema>) => { const onSubmit = async (data: z.infer<typeof FormSchema>) => {
MySwal.fire({ MySwal.fire({
@ -77,9 +102,85 @@ export default function AddExpertForm() {
const save = async (data: z.infer<typeof FormSchema>) => { const save = async (data: z.infer<typeof FormSchema>) => {
console.log("data", data); console.log("data", data);
// successSubmit(); const dataReq = {
firstName: data.name,
username: data.username,
email: data.email,
password: data.password,
adress: "",
roleId: "EXP-ID",
phoneNumber: data.phoneNumber,
userCompetencyId: data.skills,
userExperienceId: data.experiences,
companyName: data.company,
}
loading();
const res = await saveUserInternal(dataReq);
const resData = res?.data?.data;
const userProfileId = resData.id;
var placementArr: any[] = [];
placementRows.forEach((row: any) => {
placementArr.push({
roleId: Number(row.roleId),
userLevelId: Number(row.userLevelId),
userProfileId: userProfileId,
});
});
const dataReq2 = {
userId: userProfileId,
placements: placementArr
}
const res2 = await saveUserRolePlacements(dataReq2);
const resData2 = res2?.data?.data;
success("/admin/add-experts");
}; };
function success(redirect: string): void {
MySwal.fire({
title: '<p class="text-green-600 font-bold">Sukses</p>',
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: '<span class="text-white">OK</span>',
allowOutsideClick: false,
}).then((result) => {
if (result.isConfirmed) {
router.push(redirect);
}
});
}
useEffect(() => {
getDataAdditional();
}, []);
async function getDataAdditional() {
const resCompetencies = await getListCompetencies();
setUserCompetencies(resCompetencies?.data?.data);
const resExperiences = await getListExperiences();
setUserExperiences(resExperiences?.data?.data);
const resUserLevels = await AdministrationLevelList();
const data = resUserLevels?.data?.data;
var levelsArr: any[] = [];
data.forEach((levels: any) => {
levelsArr.push({
id: levels.id,
label: levels.name,
name: levels.name,
value: String(levels.id),
levelNumber: levels.levelNumber,
});
});
setUserLevels(levelsArr);
}
function successSubmit() { function successSubmit() {
MySwal.fire({ MySwal.fire({
title: "Sukses", title: "Sukses",
@ -92,6 +193,29 @@ export default function AddExpertForm() {
} }
}); });
} }
const handleSelectionChange = (index: number, type: "roleId" | "userLevelId", value: string) => {
setPlacementRows((prevRows) =>
prevRows.map((row) =>
row.index === index ? { ...row, [type]: value } : row
)
);
};
const handleRemoveRow = (index: number) => {
console.log(index);
console.log(placementRows);
const newPlacements = placementRows.filter((row) => row.index != index);
console.log(newPlacements);
setPlacementRows(newPlacements);
};
const handleAddRow = () => {
setPlacementRows((prevRows: any) => [...prevRows, { index: incrementId, roleId: "", userLevelId: 0 }]);
setIncrementId((prevId) => prevId + 1);
};
return ( return (
<div> <div>
<SiteBreadcrumb /> <SiteBreadcrumb />
@ -118,6 +242,22 @@ export default function AddExpertForm() {
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Username"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="phoneNumber" name="phoneNumber"
@ -152,45 +292,16 @@ export default function AddExpertForm() {
/> />
<FormField <FormField
control={form.control} control={form.control}
name="position" name="password"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Posisi</FormLabel> <FormLabel>Password</FormLabel>
<Select onValueChange={field.onChange} value={field.value}> <Input
<FormControl> type="password"
<SelectTrigger> value={field.value}
<SelectValue placeholder="Pilih Region" /> placeholder="Masukkan Password"
</SelectTrigger> onChange={field.onChange}
</FormControl> />
<SelectContent>
<SelectItem value="koor-kurator">Koor Kurator</SelectItem>
<SelectItem value="kurator">Kurator</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="region"
render={({ field }) => (
<FormItem>
<FormLabel>Wilayah</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Pilih Region" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="nasional">Nasional</SelectItem>
<SelectItem value="jakarta">DKI Jakarta</SelectItem>
<SelectItem value="jabar">Jawa Barat</SelectItem>
</SelectContent>
</Select>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
@ -208,19 +319,20 @@ export default function AddExpertForm() {
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
<SelectItem value="komunikasi">Komunikasi</SelectItem> {userCompetencies?.map((item: any) => (
<SelectItem value="hukum">Hukum</SelectItem> <SelectItem key={item.id} value={String(item.id)}>
<SelectItem value="bahasa">Bahasa</SelectItem> {item.name}
</SelectItem>
))}
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="experience" name="experiences"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Pengalaman</FormLabel> <FormLabel>Pengalaman</FormLabel>
@ -231,15 +343,13 @@ export default function AddExpertForm() {
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
<SelectItem value="akademisi">Akademisi</SelectItem> {userExperiences?.map((item: any) => (
<SelectItem value="praktisi">Praktisi</SelectItem> <SelectItem key={item.id} value={String(item.id)}>
<SelectItem value="akademisi+praktisi"> {item.name}
Akademisi + Praktisi </SelectItem>
</SelectItem> ))}
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage />
</FormItem> </FormItem>
)} )}
/> />
@ -261,6 +371,59 @@ export default function AddExpertForm() {
)} )}
/> />
<div className="mt-4">
<FormLabel>Penempatan</FormLabel>
{placementRows?.map((row: any) => (
<div key={row.index} className="flex items-center gap-2 my-2">
<Select onValueChange={(e) => handleSelectionChange(row.index, "roleId", e)} >
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Pilih Role" />
</SelectTrigger>
</FormControl>
<SelectContent>
{roleSelection?.map((item: any) => (
<SelectItem key={item.id} value={String(item.id)}>
{item.name}
</SelectItem>
))}
</SelectContent>
</Select>
<Select onValueChange={(e) => handleSelectionChange(row.index, "userLevelId", e)}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Pilih User Level" />
</SelectTrigger>
</FormControl>
<SelectContent>
{userLevels?.map((item: any) => (
<SelectItem key={item.id} value={String(item.id)}>
{item.name}
</SelectItem>
))}
</SelectContent>
</Select>
{placementRows.length > 1 && (
<Button
type="button"
size="md"
color="destructive"
onClick={() => handleRemoveRow(row.index)}
>
Hapus
</Button>
)}
</div>
))}
<Button
type="button"
size="md"
onClick={() => handleAddRow()}
>
Tambah
</Button>
</div>
<div className="flex flex-row justify-end gap-2 mt-4 pt-4"> <div className="flex flex-row justify-end gap-2 mt-4 pt-4">
<Button <Button
size="md" size="md"

View File

@ -5,7 +5,7 @@ export default function PerformancePolda() {
return ( return (
<div> <div>
<SiteBreadcrumb /> <SiteBreadcrumb />
<p className="font-semibold">PERFORMANCE KUMULATIF PER POLDA</p> <p className="font-semibold">PERFORMANCE KUMULATIF PER POLRES</p>
<PerformancePolresViz /> <PerformancePolresViz />
</div> </div>
); );

View File

@ -0,0 +1,13 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import PerformancePolresViz from "@/components/visualization/performance-polres";
import PerformanceSatkerViz from "@/components/visualization/performance-satker";
export default function PerformanceSatker() {
return (
<div>
<SiteBreadcrumb />
<p className="font-semibold">PERFORMANCE KUMULATIF PER SATKER</p>
<PerformanceSatkerViz />
</div>
);
}

View File

@ -14,9 +14,7 @@ export default function AdminBanner() {
<SiteBreadcrumb /> <SiteBreadcrumb />
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3"> <div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between"> <div className="flex justify-between">
{selectedTab === "content" {selectedTab === "content" ? "List Media" : " List Banner"}
? "Daftar List Media"
: "Table List Banner"}
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5"> <div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button <Button

View File

@ -39,6 +39,7 @@ import Image from "next/image";
import { Upload } from "tus-js-client"; import { Upload } from "tus-js-client";
import { getCookiesDecrypt } from "@/lib/utils"; import { getCookiesDecrypt } from "@/lib/utils";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { useTranslations } from "next-intl";
const FormSchema = z.object({ const FormSchema = z.object({
title: z.string({ title: z.string({
@ -109,7 +110,7 @@ export default function CreateCategoryModal() {
const levelNumber = getCookiesDecrypt("ulne"); const levelNumber = getCookiesDecrypt("ulne");
const userLevelId = getCookiesDecrypt("ulie"); const userLevelId = getCookiesDecrypt("ulie");
const poldaState = Cookies.get("state"); const poldaState = Cookies.get("state");
const t = useTranslations("Menu");
const [files, setFiles] = useState<File[]>([]); const [files, setFiles] = useState<File[]>([]);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [satkerData, setSatkerData] = useState<string[]>([]); const [satkerData, setSatkerData] = useState<string[]>([]);
@ -239,12 +240,12 @@ export default function CreateCategoryModal() {
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button color="primary" size="md"> <Button color="primary" size="md">
Tambah Kategori {t("add-category")}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent size="md"> <DialogContent size="md">
<DialogHeader> <DialogHeader>
<DialogTitle>Tambah Kategori</DialogTitle> <DialogTitle> {t("add-category")}</DialogTitle>
</DialogHeader> </DialogHeader>
<Form {...form}> <Form {...form}>
<form <form

View File

@ -44,10 +44,12 @@ import {
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import CreateCategoryModal from "./create"; import CreateCategoryModal from "./create";
import { useTranslations } from "next-intl";
const AdminCategoryTable = () => { const AdminCategoryTable = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const t = useTranslations("Menu");
const dataChange = searchParams?.get("dataChange"); const dataChange = searchParams?.get("dataChange");
const [openModal, setOpenModal] = React.useState(false); const [openModal, setOpenModal] = React.useState(false);
const [dataTable, setDataTable] = React.useState<any[]>([]); const [dataTable, setDataTable] = React.useState<any[]>([]);
@ -127,7 +129,7 @@ const AdminCategoryTable = () => {
return ( return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3"> <div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center"> <div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">Kategori</p> <p className="text-xl font-medium text-default-900">{t("category")}</p>
<CreateCategoryModal /> <CreateCategoryModal />
</div> </div>

View File

@ -40,6 +40,7 @@ import { useDropzone } from "react-dropzone";
import { CloudUpload } from "lucide-react"; import { CloudUpload } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { Upload } from "tus-js-client"; import { Upload } from "tus-js-client";
import { useTranslations } from "next-intl";
const FormSchema = z.object({ const FormSchema = z.object({
answer: z.string({ answer: z.string({
@ -79,7 +80,7 @@ const publishToList = [
export default function CreateFAQModal() { export default function CreateFAQModal() {
const router = useRouter(); const router = useRouter();
const { toast } = useToast(); const { toast } = useToast();
const t = useTranslations("Menu");
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [satkerData, setSatkerData] = useState<string[]>([]); const [satkerData, setSatkerData] = useState<string[]>([]);
const [unitData, setUnitData] = useState<string[]>([]); const [unitData, setUnitData] = useState<string[]>([]);
@ -120,12 +121,12 @@ export default function CreateFAQModal() {
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button color="primary" size="md"> <Button color="primary" size="md">
Tambah FAQ {t("add")} FAQ
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent size="md"> <DialogContent size="md">
<DialogHeader> <DialogHeader>
<DialogTitle>Tambah FAQ</DialogTitle> <DialogTitle>{t("add")} FAQ</DialogTitle>
</DialogHeader> </DialogHeader>
<Form {...form}> <Form {...form}>
<form <form

View File

@ -41,6 +41,7 @@ import { useDropzone } from "react-dropzone";
import { CloudUpload } from "lucide-react"; import { CloudUpload } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { Upload } from "tus-js-client"; import { Upload } from "tus-js-client";
import { useTranslations } from "next-intl";
const FormSchema = z.object({ const FormSchema = z.object({
question: z.string({ question: z.string({
@ -77,7 +78,7 @@ const publishToList = [
export default function CreateFAQModal() { export default function CreateFAQModal() {
const router = useRouter(); const router = useRouter();
const { toast } = useToast(); const { toast } = useToast();
const t = useTranslations("Menu");
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({ const form = useForm<z.infer<typeof FormSchema>>({
@ -110,12 +111,12 @@ export default function CreateFAQModal() {
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button color="primary" size="md"> <Button color="primary" size="md">
Tambah Feedback {t("add")} Feedback
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent size="md"> <DialogContent size="md">
<DialogHeader> <DialogHeader>
<DialogTitle>Tambah Feedback</DialogTitle> <DialogTitle>{t("add")} Feedback</DialogTitle>
</DialogHeader> </DialogHeader>
<Form {...form}> <Form {...form}>
<form <form

View File

@ -41,11 +41,13 @@ import {
} from "@/service/settings/settings"; } from "@/service/settings/settings";
import CreateFAQModal from "./create"; import CreateFAQModal from "./create";
import { useTranslations } from "next-intl";
const AdminFeedbackTable = () => { const AdminFeedbackTable = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange"); const dataChange = searchParams?.get("dataChange");
const [openModal, setOpenModal] = React.useState(false); const [openModal, setOpenModal] = React.useState(false);
const [dataTable, setDataTable] = React.useState<any[]>([]); const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1); const [totalData, setTotalData] = React.useState<number>(1);

View File

@ -42,6 +42,7 @@ import {
CommandList, CommandList,
} from "@/components/ui/command"; } from "@/components/ui/command";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
const FormSchema = z.object({ const FormSchema = z.object({
name: z.string({ name: z.string({
@ -55,6 +56,7 @@ const FormSchema = z.object({
export default function CreateTagModal() { export default function CreateTagModal() {
const router = useRouter(); const router = useRouter();
const { toast } = useToast(); const { toast } = useToast();
const t = useTranslations("Menu");
const [categoryList, setCategoryList] = useState< const [categoryList, setCategoryList] = useState<
{ id: number; label: string; value: string }[] { id: number; label: string; value: string }[]
>([]); >([]);
@ -107,12 +109,12 @@ export default function CreateTagModal() {
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button color="primary" size="md"> <Button color="primary" size="md">
Tambah Tag {t("add-tags")}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent size="md"> <DialogContent size="md">
<DialogHeader> <DialogHeader>
<DialogTitle>Tambah Tag</DialogTitle> <DialogTitle> {t("add-tags")}</DialogTitle>
</DialogHeader> </DialogHeader>
<Form {...form}> <Form {...form}>
<form <form

View File

@ -42,10 +42,12 @@ import {
} from "@/service/settings/settings"; } from "@/service/settings/settings";
import CreateFAQModal from "./create"; import CreateFAQModal from "./create";
import { useTranslations } from "next-intl";
const AdminTagTable = () => { const AdminTagTable = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const t = useTranslations("Menu");
const dataChange = searchParams?.get("dataChange"); const dataChange = searchParams?.get("dataChange");
const [openModal, setOpenModal] = React.useState(false); const [openModal, setOpenModal] = React.useState(false);
const [dataTable, setDataTable] = React.useState<any[]>([]); const [dataTable, setDataTable] = React.useState<any[]>([]);
@ -124,7 +126,7 @@ const AdminTagTable = () => {
return ( return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3"> <div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center"> <div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">Tag</p> <p className="text-xl font-medium text-default-900">{t("tags")}</p>
<CreateFAQModal /> <CreateFAQModal />
</div> </div>

View File

@ -119,6 +119,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
); );
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null); const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
const roleId = Number(getCookiesDecrypt("urie")) || 0; const roleId = Number(getCookiesDecrypt("urie")) || 0;
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
console.log("roleId", roleId);
console.log("userlevel", userLevelId);
const [apiEvents, setApiEvents] = useState<CalendarEvent[]>([]); const [apiEvents, setApiEvents] = useState<CalendarEvent[]>([]);
const [Isloading, setLoading] = useState<boolean>(false); const [Isloading, setLoading] = useState<boolean>(false);
const [draggableInitialized, setDraggableInitialized] = const [draggableInitialized, setDraggableInitialized] =
@ -574,11 +577,11 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<div className="grid grid-cols-12 gap-6 divide-x divide-border"> <div className="grid grid-cols-12 gap-6 divide-x divide-border">
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5"> <Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
<CardContent className="p-0"> <CardContent className="p-0">
<CardHeader className="border-none mb-2 pt-5"> <CardHeader className="border-none mb-2 pt-5 ">
{roleId == 3 || roleId == 11 || roleId == 2 || roleId == 12 ? ( {roleId == 3 || roleId == 11 || roleId == 2 || roleId == 12 ? (
<Button <Button
onClick={handleDateClick} onClick={handleDateClick}
className="dark:bg-background dark:text-foreground" className="dark:bg-background dark:text-foreground w-[250px]"
> >
<Plus className="w-4 h-4 me-1" /> <Plus className="w-4 h-4 me-1" />
{t("addEvent")} {t("addEvent")}
@ -589,8 +592,8 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<div> <div>
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
{roleId == 2 ? ( {roleId === 3 && userLevelId === 216 ? (
<Button className="dark:bg-background dark:text-foreground "> <Button className="dark:bg-background dark:text-foreground w-[250px]">
<Book className="w-4 h-4" /> <Book className="w-4 h-4" />
{t("bag-pa-monitoring-results")} {t("bag-pa-monitoring-results")}
</Button> </Button>

View File

@ -45,6 +45,7 @@ import { listEnableCategory } from "@/service/content/content";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { CardHeader, CardTitle } from "@/components/ui/card"; import { CardHeader, CardTitle } from "@/components/ui/card";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import useTableColumns from "./columns";
const BlogTable = () => { const BlogTable = () => {
const router = useRouter(); const router = useRouter();
@ -73,7 +74,7 @@ const BlogTable = () => {
); );
const [categoryFilter, setCategoryFilter] = React.useState<string>(""); const [categoryFilter, setCategoryFilter] = React.useState<string>("");
const [statusFilter, setStatusFilter] = React.useState<any[]>([]); const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -17,155 +17,162 @@ import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { deleteBlog } from "@/service/blog/blog"; import { deleteBlog } from "@/service/blog/blog";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Title",
cell: ({ row }) => (
<span className="whitespace-normal">{row.getValue("title")}</span>
),
},
{
accessorKey: "categoryName",
header: "Category",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
{
accessorKey: "createdAt",
header: "Upload Date",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate = const columns: ColumnDef<any>[] = [
createdAt && !isNaN(new Date(createdAt).getTime()) {
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") accessorKey: "no",
: "-"; header: t("no"),
return <span className="whitespace-nowrap">{formattedDate}</span>; cell: ({ row }) => <span>{row.getValue("no")}</span>,
}, },
}, {
{ accessorKey: "title",
accessorKey: "tags", header: t("title"),
header: "Tag", cell: ({ row }) => (
cell: ({ row }) => <span className="">{row.getValue("tags")}</span>, <span className="whitespace-normal">{row.getValue("title")}</span>
}, ),
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
// Gunakan `statusName` untuk pencocokan
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge className={cn("rounded-full px-5", statusStyles)}>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
}, },
}, {
accessorKey: "categoryName",
header: t("category"),
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
{
accessorKey: "createdAt",
header: t("upload-date"),
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
{ const formattedDate =
id: "actions", createdAt && !isNaN(new Date(createdAt).getTime())
accessorKey: "action", ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
header: "Actions", : "-";
enableHiding: false, return <span className="whitespace-nowrap">{formattedDate}</span>;
cell: ({ row }) => { },
const router = useRouter(); },
const MySwal = withReactContent(Swal); {
accessorKey: "tags",
header: t("tag"),
cell: ({ row }) => <span className="">{row.getValue("tags")}</span>,
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
async function deleteProcess(id: any) { // Mengambil `statusName` dari data API
loading(); const status = row.getValue("statusName") as string;
const resDelete = await deleteBlog(id); const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
if (resDelete?.error) { // Gunakan `statusName` untuk pencocokan
error(resDelete.message); const statusStyles =
return false; statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge className={cn("rounded-full px-5", statusStyles)}>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const MySwal = withReactContent(Swal);
async function deleteProcess(id: any) {
loading();
const resDelete = await deleteBlog(id);
if (resDelete?.error) {
error(resDelete.message);
return false;
}
success();
} }
success();
}
function success() { function success() {
MySwal.fire({ MySwal.fire({
title: "Sukses", title: "Sukses",
icon: "success", icon: "success",
confirmButtonColor: "#3085d6", confirmButtonColor: "#3085d6",
confirmButtonText: "OK", confirmButtonText: "OK",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
window.location.reload(); window.location.reload();
} }
}); });
} }
const handleDeleteBlog = (id: any) => { const handleDeleteBlog = (id: any) => {
MySwal.fire({ MySwal.fire({
title: "Hapus Data", title: "Hapus Data",
text: "", text: "",
icon: "warning", icon: "warning",
showCancelButton: true, showCancelButton: true,
cancelButtonColor: "#3085d6", cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33", confirmButtonColor: "#d33",
confirmButtonText: "Hapus", confirmButtonText: "Hapus",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
deleteProcess(id); deleteProcess(id);
} }
}); });
}; };
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
size="icon" size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent" className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
> >
<span className="sr-only">Open menu</span> <span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" /> <MoreVertical className="h-4 w-4 text-default-800" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end"> <DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/blog/detail/${row.original.id}`}> <Link href={`/contributor/blog/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" /> <Eye className="w-4 h-4 me-1.5" />
View View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/blog/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteBlog(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
<Link href={`/contributor/blog/update/${row.original.id}`}> </DropdownMenu>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> );
<SquarePen className="w-4 h-4 me-1.5" /> },
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteBlog(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -17,211 +17,224 @@ import withReactContent from "sweetalert2-react-content";
import { deleteMedia } from "@/service/content/content"; import { deleteMedia } from "@/service/content/content";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const MySwal = withReactContent(Swal);
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "title",
accessorKey: "title", header: t("title"),
header: "Title", cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { const title: string = row.getValue("title");
const title: string = row.getValue("title"); return (
return ( <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "categoryName",
header: t("category-name"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("categoryName")}
</span> </span>
); ),
}, },
}, {
{ accessorKey: "createdAt",
accessorKey: "categoryName", header: t("upload-date"),
header: "Category Name", cell: ({ row }) => {
cell: ({ row }) => ( const createdAt = row.getValue("createdAt") as
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span> | string
), | number
}, | undefined;
{
accessorKey: "createdAt",
header: "Upload Date",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate = const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime()) createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-"; : "-";
return <span className="whitespace-nowrap">{formattedDate}</span>; return <span className="whitespace-nowrap">{formattedDate}</span>;
},
}, },
}, {
{ accessorKey: "creatorName",
accessorKey: "creatorName", header: t("creator-group"),
header: "Creator Group", cell: ({ row }) => (
cell: ({ row }) => ( <span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span> ),
),
},
{
accessorKey: "creatorGroupLevelName",
header: "Sumber",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
</span>
),
},
{
accessorKey: "publishedOn",
header: "Published",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
}, },
}, {
{ accessorKey: "creatorGroupLevelName",
accessorKey: "statusName", header: t("source"),
header: "Status", cell: ({ row }) => (
cell: ({ row }) => { <span className="whitespace-nowrap">
const statusColors: Record<string, string> = { {row.getValue("creatorGroupLevelName")}
diterima: "bg-green-100 text-green-600", </span>
"menunggu review": "bg-orange-100 text-orange-600", ),
};
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
// Gunakan `statusName` untuk pencocokan
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge
className={cn(
"rounded-full px-5 w-full whitespace-nowrap",
statusStyles
)}
>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
}, },
}, {
{ accessorKey: "publishedOn",
id: "actions", header: t("published"),
accessorKey: "action", cell: ({ row }) => {
header: "Actions", const isPublish = row.original.isPublish;
enableHiding: false, const isPublishOnPolda = row.original.isPublishOnPolda;
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
async function doDelete(id: any) { let displayText = "-";
// loading(); if (isPublish && !isPublishOnPolda) {
const data = { displayText = "Mabes";
id, } else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
}; };
const response = await deleteMedia(data); // Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
if (response?.error) { // Gunakan `statusName` untuk pencocokan
error(response.message); const statusStyles =
return false; statusColors[statusName] || "bg-gray-100 text-gray-600";
}
success();
}
function success() { return (
MySwal.fire({ <Badge
title: "Sukses", className={cn(
icon: "success", "rounded-full px-5 w-full whitespace-nowrap",
confirmButtonColor: "#3085d6", statusStyles
confirmButtonText: "OK", )}
}).then((result) => { >
if (result.isConfirmed) { {status} {/* Tetap tampilkan nilai asli */}
window.location.reload(); </Badge>
} );
}); },
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/content/audio/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/content/audio/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, {
]; id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
export default columns; async function doDelete(id: any) {
// loading();
const data = {
id,
};
const response = await deleteMedia(data);
if (response?.error) {
error(response.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/content/audio/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link
href={`/contributor/content/audio/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -61,6 +61,7 @@ import {
} from "@/service/content/content"; } from "@/service/content/content";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { format } from "date-fns"; import { format } from "date-fns";
import useTableColumns from "./columns";
const TableAudio = () => { const TableAudio = () => {
const router = useRouter(); const router = useRouter();
@ -99,7 +100,7 @@ const TableAudio = () => {
const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState(""); const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState("");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -1,3 +1,4 @@
"use client";
import * as React from "react"; import * as React from "react";
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
@ -19,237 +20,224 @@ import { deleteMedia } from "@/service/content/content";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { useTranslations } from "next-intl";
const MySwal = withReactContent(Swal); const useTableColumns = () => {
const t = useTranslations("Table"); // Panggil di dalam hook
const columns: ColumnDef<any>[] = [ const MySwal = withReactContent(Swal);
{ const columns: ColumnDef<any>[] = [
accessorKey: "no", {
header: "No", accessorKey: "no",
cell: ({ row }) => ( header: t("no"),
<div className="flex items-center gap-5"> cell: ({ row }) => (
<div className="flex-1 text-start"> <div className="flex items-center gap-5">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> <div className="flex-1 text-start">
{row.getValue("no")} <h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
</h4> {row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "title",
accessorKey: "title", header: t("title"),
header: "Title", cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { const title: string = row.getValue("title");
const title: string = row.getValue("title"); return (
return ( <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "categoryName",
header: t("category-name"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("categoryName")}
</span> </span>
); ),
}, },
}, {
{ accessorKey: "createdAt",
accessorKey: "categoryName", header: t("upload-date"),
header: "Category Name", cell: ({ row }) => {
cell: ({ row }) => ( const createdAt = row.getValue("createdAt") as
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span> | string
), | number
}, | undefined;
{
accessorKey: "createdAt",
header: "Upload Date",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate = const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime()) createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-"; : "-";
return <span className="whitespace-nowrap">{formattedDate}</span>; return <span className="whitespace-nowrap">{formattedDate}</span>;
},
}, },
}, {
{ accessorKey: "creatorName",
accessorKey: "creatorName", header: t("creator-group"),
header: "Creator Group", cell: ({ row }) => (
cell: ({ row }) => ( <span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span> ),
),
},
{
accessorKey: "creatorGroupLevelName",
header: "Sumber",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
</span>
),
},
{
accessorKey: "publishedOn",
header: "Published",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
}, },
}, {
{ accessorKey: "creatorGroupLevelName",
accessorKey: "statusId", header: t("source"),
header: "Status", cell: ({ row }) => (
cell: ({ row }) => { <span className="whitespace-nowrap">
const userLevelId = 2; // Gantilah sesuai dengan konteks aplikasi {row.getValue("creatorGroupLevelName")}
const statusId = Number(row.getValue("statusId")); </span>
const reviewedAtLevel = row.original.reviewedAtLevel as string | null; ),
const needApprovalFromLevel = Number(row.original.needApprovalFromLevel);
let statusName = row.getValue("statusName") as string;
let icon = "fa:times-circle";
let statusClass = "bg-gray-100 text-gray-600";
if (
(statusId === 2 &&
reviewedAtLevel !== null &&
!reviewedAtLevel.includes(`:${userLevelId}:`)) ||
(statusId === 1 && needApprovalFromLevel === userLevelId)
) {
statusName = "Menunggu Review";
icon = "fa:hourglass-end";
statusClass = "bg-orange-100 text-orange-600";
} else if (
statusId === 2 &&
reviewedAtLevel?.includes(`:${userLevelId}:`)
) {
icon = "fa:check-circle";
statusClass = "bg-green-100 text-green-600";
} else if (statusId === 2) {
icon = "fa:check-circle";
statusClass = "bg-green-100 text-green-600";
} else if (statusId === 3) {
icon = "fa:comment";
statusClass = "bg-blue-100 text-blue-600";
} else if (statusId === 1) {
icon = "fa:hourglass-end";
statusClass = "bg-orange-100 text-orange-600";
}
return (
<Badge
className={cn(
"rounded-full px-5 w-full whitespace-nowrap",
statusClass
)}
>
{statusName}
</Badge>
);
}, },
}, {
accessorKey: "publishedOn",
header: t("published"),
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
{ let displayText = "-";
id: "actions", if (isPublish && !isPublishOnPolda) {
accessorKey: "action", displayText = "Mabes";
header: "Actions", } else if (isPublish && isPublishOnPolda) {
enableHiding: false, displayText = "Mabes & Polda";
cell: ({ row }) => { } else if (!isPublish && isPublishOnPolda) {
const router = useRouter(); displayText = "Polda";
const MySwal = withReactContent(Swal); }
async function doDelete(id: any) { return (
// loading(); <div className="text-center whitespace-nowrap" title={displayText}>
const data = { {displayText}
id, </div>
);
},
},
//
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
}; };
const response = await deleteMedia(data); const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase();
const statusStyles =
statusColors[statusName] || "bg-red-200 text-red-600";
if (response?.error) { return (
error(response.message); <Badge
return false; className={cn(
} "rounded-full px-5 w-full whitespace-nowrap",
success(); statusStyles
} )}
>
function success() { {status} {/* Tetap tampilkan nilai asli */}
MySwal.fire({ </Badge>
title: "Sukses", );
icon: "success", },
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/content/image/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/content/image/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
},
];
export default columns; {
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const MySwal = withReactContent(Swal);
async function doDelete(id: any) {
// loading();
const data = {
id,
};
const response = await deleteMedia(data);
if (response?.error) {
error(response.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/content/image/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link
href={`/contributor/content/image/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -52,7 +52,7 @@ import { ticketingPagination } from "@/service/ticketing/ticketing";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination"; import TablePagination from "@/components/table/table-pagination";
import columns from "./columns";
import { import {
deleteMedia, deleteMedia,
listDataImage, listDataImage,
@ -66,6 +66,7 @@ import withReactContent from "sweetalert2-react-content";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { format } from "date-fns"; import { format } from "date-fns";
import useTableColumns from "./columns";
const TableImage = () => { const TableImage = () => {
const router = useRouter(); const router = useRouter();
@ -104,7 +105,7 @@ const TableImage = () => {
const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState(""); const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState("");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -16,137 +16,146 @@ import {
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const MySwal = withReactContent(Swal);
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "contentTitle",
accessorKey: "contentTitle", header: t("title"),
header: "Judul", cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { const title: string = row.getValue("contentTitle");
const title: string = row.getValue("contentTitle"); return (
return ( <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "contentTag",
header: t("tag"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("contentTag")}</span>
),
},
{
accessorKey: "contentType",
header: t("type-content"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("contentType")}</span>
),
},
{
accessorKey: "contentCreatedGroupBy",
header: t("source"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("contentCreatedGroupBy")}
</span> </span>
); ),
}, },
},
{
accessorKey: "contentTag",
header: "Tag",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("contentTag")}</span>
),
},
{ {
accessorKey: "contentType", accessorKey: "isPublish",
header: "Tipe Konten ", header: "Status",
cell: ({ row }) => ( cell: ({ row }) => {
<span className="whitespace-nowrap">{row.getValue("contentType")}</span> const isPublish = row.getValue<boolean>("isPublish");
), return (
}, <div>
{
accessorKey: "contentCreatedGroupBy",
header: "Sumber ",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("contentCreatedGroupBy")}
</span>
),
},
{
accessorKey: "isPublish",
header: "Status",
cell: ({ row }) => {
const isPublish = row.getValue<boolean>("isPublish");
return (
<div>
<Button
size="sm"
color={isPublish ? "success" : "warning"}
variant="outline"
className={`btn btn-sm ${
isPublish ? "btn-outline-success" : "btn-outline-warning"
} pill-btn ml-1`}
>
{isPublish ? "Diterima" : "Menunggu Review"}
</Button>
</div>
);
},
},
{
accessorKey: "contentCreatedDate",
header: "Tanggal Unggah",
cell: ({ row }) => {
const createdAt = row.getValue("contentCreatedDate") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const isDisabled = row.original.isPublish; // Check the isPublish value
return (
<DropdownMenu>
<DropdownMenuTrigger asChild disabled={isDisabled}>
<Button <Button
size="icon" size="sm"
className={`bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent ${ color={isPublish ? "success" : "warning"}
isDisabled ? "cursor-not-allowed opacity-50" : "" variant="outline"
}`} className={`btn btn-sm ${
disabled={isDisabled} // Disable button if isPublish is true isPublish ? "btn-outline-success" : "btn-outline-warning"
} pill-btn ml-1`}
> >
<span className="sr-only">Open menu</span> {isPublish ? "Diterima" : "Menunggu Review"}
<MoreVertical className="h-4 w-4 text-default-800" />
</Button> </Button>
</DropdownMenuTrigger> </div>
<DropdownMenuContent className="p-0" align="end"> );
<Link },
href={`/contributor/content/spit/convert/${row.original.contentId}`} },
>
<DropdownMenuItem {
className={`p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none ${ accessorKey: "contentCreatedDate",
header: t("upload-date"),
cell: ({ row }) => {
const createdAt = row.getValue("contentCreatedDate") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const isDisabled = row.original.isPublish; // Check the isPublish value
return (
<DropdownMenu>
<DropdownMenuTrigger asChild disabled={isDisabled}>
<Button
size="icon"
className={`bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent ${
isDisabled ? "cursor-not-allowed opacity-50" : "" isDisabled ? "cursor-not-allowed opacity-50" : ""
}`} }`}
disabled={isDisabled} // Disable dropdown item if isPublish is true disabled={isDisabled} // Disable button if isPublish is true
> >
<MoveDownRight className="w-4 h-4 me-1.5" /> <span className="sr-only">Open menu</span>
Pindah Ke Mediahub <MoreVertical className="h-4 w-4 text-default-800" />
</DropdownMenuItem> </Button>
</Link> </DropdownMenuTrigger>
</DropdownMenuContent> <DropdownMenuContent className="p-0" align="end">
</DropdownMenu> <Link
); href={`/contributor/content/spit/convert/${row.original.contentId}`}
>
<DropdownMenuItem
className={`p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none ${
isDisabled ? "cursor-not-allowed opacity-50" : ""
}`}
disabled={isDisabled} // Disable dropdown item if isPublish is true
>
<MoveDownRight className="w-4 h-4 me-1.5" />
Pindah Ke Mediahub
</DropdownMenuItem>
</Link>
</DropdownMenuContent>
</DropdownMenu>
);
},
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -47,6 +47,9 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { useTranslations } from "next-intl";
import { format } from "date-fns";
import useTableColumns from "./columns";
// export type CompanyData = { // export type CompanyData = {
// no: number; // no: number;
@ -77,7 +80,8 @@ const TableSPIT = () => {
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
const userId = getCookiesDecrypt("uie"); const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie"); const userLevelId = getCookiesDecrypt("ulie");
const t = useTranslations("AnalyticsDashboard");
const [dateFilter, setDateFilter] = React.useState("");
const [totalData, setTotalData] = React.useState<number>(1); const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]); const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>( const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
@ -86,7 +90,7 @@ const TableSPIT = () => {
const [statusFilter, setStatusFilter] = React.useState<any[]>([]); const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: spitTable, data: spitTable,
columns, columns,
@ -117,7 +121,7 @@ const TableSPIT = () => {
React.useEffect(() => { React.useEffect(() => {
fetchData(); fetchData();
}, [page, limit, search, statusFilter]); }, [page, limit, search, statusFilter, dateFilter]);
async function fetchData() { async function fetchData() {
let isPublish; let isPublish;
@ -129,8 +133,18 @@ const TableSPIT = () => {
isPublish = statusFilter.includes(1) ? false : true; isPublish = statusFilter.includes(1) ? false : true;
} }
const formattedStartDate = dateFilter
? format(new Date(dateFilter), "yyyy-MM-dd")
: "";
try { try {
const res = await listSPIT(page - 1, limit, search, isPublish); const res = await listSPIT(
page - 1,
limit,
search,
formattedStartDate,
isPublish
);
const data = res?.data?.data; const data = res?.data?.data;
const contentData = data?.content || []; const contentData = data?.content || [];
@ -192,6 +206,15 @@ const TableSPIT = () => {
<div className="flex flex-row justify-between my-1 mx-1"> <div className="flex flex-row justify-between my-1 mx-1">
<p>Filter</p> <p>Filter</p>
</div> </div>
<div className="mx-2 my-1">
<Label>{t("date")}</Label>
<Input
type="date"
value={dateFilter}
onChange={(e) => setDateFilter(e.target.value)}
className="max-w-sm"
/>
</div>
<Label className="ml-2 mt-2">Status</Label> <Label className="ml-2 mt-2">Status</Label>
<div className="flex items-center px-4 py-1"> <div className="flex items-center px-4 py-1">
<input <input

View File

@ -17,212 +17,224 @@ import { error } from "@/lib/swal";
import { deleteMedia } from "@/service/content/content"; import { deleteMedia } from "@/service/content/content";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const MySwal = withReactContent(Swal);
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "title",
accessorKey: "title", header: t("title"),
header: "Title", cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { const title: string = row.getValue("title");
const title: string = row.getValue("title"); return (
return ( <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "categoryName",
header: t("category-name"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("categoryName")}
</span> </span>
); ),
}, },
}, {
{ accessorKey: "createdAt",
accessorKey: "categoryName", header: t("upload-date"),
header: "Category Name", cell: ({ row }) => {
cell: ({ row }) => ( const createdAt = row.getValue("createdAt") as
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span> | string
), | number
}, | undefined;
{
accessorKey: "createdAt",
header: "Upload Date",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate = const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime()) createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-"; : "-";
return <span className="whitespace-nowrap">{formattedDate}</span>; return <span className="whitespace-nowrap">{formattedDate}</span>;
},
}, },
}, {
{ accessorKey: "creatorName",
accessorKey: "creatorName", header: t("creator-group"),
header: "Creator Group", cell: ({ row }) => (
cell: ({ row }) => ( <span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span> ),
),
},
{
accessorKey: "creatorGroupLevelName",
header: "Sumber",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
</span>
),
},
{
accessorKey: "publishedOn",
header: "Published",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
}, },
}, {
accessorKey: "creatorGroupLevelName",
{ header: t("source"),
accessorKey: "statusName", cell: ({ row }) => (
header: "Status", <span className="whitespace-nowrap">
cell: ({ row }) => { {row.getValue("creatorGroupLevelName")}
const statusColors: Record<string, string> = { </span>
diterima: "bg-green-100 text-green-600", ),
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
// Gunakan `statusName` untuk pencocokan
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge
className={cn(
"rounded-full px-5 w-full whitespace-nowrap",
statusStyles
)}
>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
}, },
}, {
{ accessorKey: "publishedOn",
id: "actions", header: t("published"),
accessorKey: "action", cell: ({ row }) => {
header: "Actions", const isPublish = row.original.isPublish;
enableHiding: false, const isPublishOnPolda = row.original.isPublishOnPolda;
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
async function doDelete(id: any) { let displayText = "-";
// loading(); if (isPublish && !isPublishOnPolda) {
const data = { displayText = "Mabes";
id, } else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
}; };
const response = await deleteMedia(data); // Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
if (response?.error) { // Gunakan `statusName` untuk pencocokan
error(response.message); const statusStyles =
return false; statusColors[statusName] || "bg-gray-100 text-gray-600";
}
success();
}
function success() { return (
MySwal.fire({ <Badge
title: "Sukses", className={cn(
icon: "success", "rounded-full px-5 w-full whitespace-nowrap",
confirmButtonColor: "#3085d6", statusStyles
confirmButtonText: "OK", )}
}).then((result) => { >
if (result.isConfirmed) { {status} {/* Tetap tampilkan nilai asli */}
window.location.reload(); </Badge>
} );
}); },
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/content/teks/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/content/teks/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, {
]; id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
export default columns; async function doDelete(id: any) {
// loading();
const data = {
id,
};
const response = await deleteMedia(data);
if (response?.error) {
error(response.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/content/teks/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link
href={`/contributor/content/teks/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -60,6 +60,7 @@ import {
} from "@/service/content/content"; } from "@/service/content/content";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { format } from "date-fns"; import { format } from "date-fns";
import useTableColumns from "./columns";
const TableTeks = () => { const TableTeks = () => {
const router = useRouter(); const router = useRouter();
@ -98,7 +99,7 @@ const TableTeks = () => {
const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState(""); const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState("");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -17,209 +17,223 @@ import { deleteMedia } from "@/service/content/content";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const MySwal = withReactContent(Swal);
header: "No",
cell: ({ row }) => ( const columns: ColumnDef<any>[] = [
<div className="flex items-center gap-5"> {
<div className="flex-1 text-start"> accessorKey: "no",
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> header: t("no"),
{row.getValue("no")} cell: ({ row }) => (
</h4> <div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "title",
accessorKey: "title", header: t("title"),
header: "Title", cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { const title: string = row.getValue("title");
const title: string = row.getValue("title"); return (
return ( <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "categoryName",
header: t("category-name"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("categoryName")}
</span> </span>
); ),
}, },
}, {
{ accessorKey: "createdAt",
accessorKey: "categoryName", header: t("upload-date"),
header: "Category Name", cell: ({ row }) => {
cell: ({ row }) => ( const createdAt = row.getValue("createdAt") as
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span> | string
), | number
}, | undefined;
{
accessorKey: "createdAt",
header: "Upload Date",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate = const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime()) createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-"; : "-";
return <span className="whitespace-nowrap">{formattedDate}</span>; return <span className="whitespace-nowrap">{formattedDate}</span>;
},
}, },
}, {
{ accessorKey: "creatorName",
accessorKey: "creatorName", header: t("creator-group"),
header: "Creator Group", cell: ({ row }) => (
cell: ({ row }) => ( <span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span> ),
),
},
{
accessorKey: "creatorGroupLevelName",
header: "Sumber",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
</span>
),
},
{
accessorKey: "publishedOn",
header: "Published",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
}, },
}, {
accessorKey: "creatorGroupLevelName",
{ header: t("source"),
accessorKey: "statusName", cell: ({ row }) => (
header: "Status", <span className="whitespace-nowrap">
cell: ({ row }) => { {row.getValue("creatorGroupLevelName")}
const statusColors: Record<string, string> = { </span>
diterima: "bg-green-100 text-green-600", ),
"menunggu review": "bg-orange-100 text-orange-600",
};
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase();
const statusStyles =
statusColors[statusName] || "bg-red-200 text-red-600";
return (
<Badge
className={cn(
"rounded-full px-5 w-full whitespace-nowrap",
statusStyles
)}
>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
}, },
}, {
{ accessorKey: "publishedOn",
id: "actions", header: t("published"),
accessorKey: "action", cell: ({ row }) => {
header: "Actions", const isPublish = row.original.isPublish;
enableHiding: false, const isPublishOnPolda = row.original.isPublishOnPolda;
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
async function doDelete(id: any) { let displayText = "-";
// loading(); if (isPublish && !isPublishOnPolda) {
const data = { displayText = "Mabes";
id, } else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
}; };
const response = await deleteMedia(data); const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase();
const statusStyles =
statusColors[statusName] || "bg-red-200 text-red-600";
if (response?.error) { return (
error(response.message); <Badge
return false; className={cn(
} "rounded-full px-5 w-full whitespace-nowrap",
success(); statusStyles
} )}
>
function success() { {status} {/* Tetap tampilkan nilai asli */}
MySwal.fire({ </Badge>
title: "Sukses", );
icon: "success", },
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/content/video/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/content/video/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, {
]; id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
export default columns; async function doDelete(id: any) {
// loading();
const data = {
id,
};
const response = await deleteMedia(data);
if (response?.error) {
error(response.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteMedia = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/content/video/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link
href={`/contributor/content/video/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteMedia(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -60,6 +60,7 @@ import {
} from "@/service/content/content"; } from "@/service/content/content";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { format } from "date-fns"; import { format } from "date-fns";
import useTableColumns from "./columns";
const TableVideo = () => { const TableVideo = () => {
const router = useRouter(); const router = useRouter();
@ -98,7 +99,7 @@ const TableVideo = () => {
const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState(""); const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState("");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -13,105 +13,111 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const columns: ColumnDef<any>[] = [
header: "No", {
cell: ({ row }) => ( accessorKey: "no",
<div className="flex items-center gap-5"> header: t("no"),
<div className="flex-1 text-start"> cell: ({ row }) => (
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> <div className="flex items-center gap-5">
{row.getValue("no")} <div className="flex-1 text-start">
</h4> <h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "title",
accessorKey: "title", header: t("title"),
header: "Judul", cell: ({ row }) => (
cell: ({ row }) => ( <div className="flex items-center gap-5">
<div className="flex items-center gap-5"> <div className="flex-1 text-start">
<div className="flex-1 text-start"> <h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> {row.getValue("title")}
{row.getValue("title")} </h4>
</h4> </div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "createdAt",
accessorKey: "createdAt", header: t("upload-date"),
header: "Tanggal Unggah ", cell: ({ row }) => {
cell: ({ row }) => { const createdAt = row.getValue("createdAt") as
const createdAt = row.getValue("createdAt") as | string
| string | number
| number | undefined;
| undefined;
const formattedDate = const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime()) createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-"; : "-";
return <span className="whitespace-nowrap">{formattedDate}</span>; return <span className="whitespace-nowrap">{formattedDate}</span>;
},
}, },
}, {
{ accessorKey: "isActive",
accessorKey: "isActive", header: "Status",
header: "Status", cell: ({ row }) => {
cell: ({ row }) => { const isActive = row.getValue<boolean>("isActive");
const isActive = row.getValue<boolean>("isActive"); console.log("isActive value:", isActive); // TypeScript type is inferred correctly
console.log("isActive value:", isActive); // TypeScript type is inferred correctly return (
return ( <div>
<div> {isActive ? (
{isActive ? ( <b className="text-blue-500">Terkirim</b>
<b className="text-blue-500">Terkirim</b> ) : (
) : ( <b className="text-danger">Belum Terkirim</b>
<b className="text-danger">Belum Terkirim</b> )}
)} </div>
</div> );
); },
}, },
}, {
{ id: "actions",
id: "actions", accessorKey: "action",
accessorKey: "action", header: t("action"),
header: "Actions", enableHiding: false,
enableHiding: false, cell: ({ row }) => {
cell: ({ row }) => { return (
return ( <DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild>
<DropdownMenuTrigger asChild> <Button
<Button size="icon"
size="icon" className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent" >
> <span className="sr-only">Open menu</span>
<span className="sr-only">Open menu</span> <MoreVertical className="h-4 w-4 text-default-800" />
<MoreVertical className="h-4 w-4 text-default-800" /> </Button>
</Button> </DropdownMenuTrigger>
</DropdownMenuTrigger> <DropdownMenuContent className="p-0" align="end">
<DropdownMenuContent className="p-0" align="end"> <Link
<Link href={`/contributor/planning/mediahub/publish/${row.original.id}`}
href={`/contributor/planning/mediahub/publish/${row.original.id}`} >
> <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> <Eye className="w-4 h-4 me-1.5" />
<Eye className="w-4 h-4 me-1.5" /> Publish
Publish </DropdownMenuItem>
</Link>
<DropdownMenuItem
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
// onClick={() => deletePlan(row.id)}
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
<DropdownMenuItem </DropdownMenu>
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" );
// onClick={() => deletePlan(row.id)} },
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -55,6 +55,7 @@ import { getPlanningSentPagination } from "@/service/planning/planning";
import search from "@/app/[locale]/(protected)/app/chat/components/search"; import search from "@/app/[locale]/(protected)/app/chat/components/search";
import { CardHeader, CardTitle } from "@/components/ui/card"; import { CardHeader, CardTitle } from "@/components/ui/card";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import useTableColumns from "./columns";
const MediahubTable = () => { const MediahubTable = () => {
const t = useTranslations("Planning"); const t = useTranslations("Planning");
@ -78,7 +79,7 @@ const MediahubTable = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -13,103 +13,109 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const columns: ColumnDef<any>[] = [
header: "No", {
cell: ({ row }) => ( accessorKey: "no",
<div className="flex items-center gap-5"> header: t("no"),
<div className="flex-1 text-start"> cell: ({ row }) => (
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> <div className="flex items-center gap-5">
{row.getValue("no")} <div className="flex-1 text-start">
</h4> <h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "title",
accessorKey: "title", header: t("title"),
header: "Judul", cell: ({ row }) => (
cell: ({ row }) => ( <div className="flex items-center gap-5">
<div className="flex items-center gap-5"> <div className="flex-1 text-start">
<div className="flex-1 text-start"> <h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> {row.getValue("title")}
{row.getValue("title")} </h4>
</h4> </div>
</div> </div>
</div> ),
), },
}, {
{ accessorKey: "createdAt",
accessorKey: "createdAt", header: t("upload-date"),
header: "Tanggal Unggah ", cell: ({ row }) => {
cell: ({ row }) => { const createdAt = row.getValue("createdAt") as
const createdAt = row.getValue("createdAt") as | string
| string | number
| number | undefined;
| undefined;
const formattedDate = const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime()) createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-"; : "-";
return <span className="whitespace-nowrap">{formattedDate}</span>; return <span className="whitespace-nowrap">{formattedDate}</span>;
},
}, },
}, {
{ accessorKey: "isActive",
accessorKey: "isActive", header: "Status",
header: "Status", cell: ({ row }) => {
cell: ({ row }) => { const isActive = row.getValue<boolean>("isActive");
const isActive = row.getValue<boolean>("isActive"); console.log("isActive value:", isActive); // TypeScript type is inferred correctly
console.log("isActive value:", isActive); // TypeScript type is inferred correctly return (
return ( <div>
<div> {isActive ? (
{isActive ? ( <b className="text-blue-500">Terkirim</b>
<b className="text-blue-500">Terkirim</b> ) : (
) : ( <b className="text-danger">Belum Terkirim</b>
<b className="text-danger">Belum Terkirim</b> )}
)} </div>
</div> );
); },
}, },
}, {
{ id: "actions",
id: "actions", accessorKey: "action",
accessorKey: "action", header: t("action"),
header: "Actions", enableHiding: false,
enableHiding: false, cell: ({ row }) => {
cell: ({ row }) => { return (
return ( <DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild>
<DropdownMenuTrigger asChild> <Button
<Button size="icon"
size="icon" className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent" >
> <span className="sr-only">Open menu</span>
<span className="sr-only">Open menu</span> <MoreVertical className="h-4 w-4 text-default-800" />
<MoreVertical className="h-4 w-4 text-default-800" /> </Button>
</Button> </DropdownMenuTrigger>
</DropdownMenuTrigger> <DropdownMenuContent className="p-0" align="end">
<DropdownMenuContent className="p-0" align="end"> <Link
<Link href={`/contributor/planning/medsos-mediahub/publish/${row.original.id}`}
href={`/contributor/planning/medsos-mediahub/publish/${row.original.id}`} >
> <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> <Eye className="w-4 h-4 me-1.5" />
<Eye className="w-4 h-4 me-1.5" /> Publish
Publish </DropdownMenuItem>
</Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
</DropdownMenu>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"> );
<Trash2 className="w-4 h-4 me-1.5" /> },
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -54,6 +54,7 @@ import columns from "./columns";
import { getPlanningSentPagination } from "@/service/planning/planning"; import { getPlanningSentPagination } from "@/service/planning/planning";
import { CardHeader, CardTitle } from "@/components/ui/card"; import { CardHeader, CardTitle } from "@/components/ui/card";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import useTableColumns from "./columns";
const MedsosTable = () => { const MedsosTable = () => {
const t = useTranslations("Planning"); const t = useTranslations("Planning");
@ -77,7 +78,7 @@ const MedsosTable = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -12,163 +12,171 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
},
{ {
accessorKey: "title", accessorKey: "title",
header: "Title", header: t("title"),
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const title: string = row.getValue("title"); const title: string = row.getValue("title");
return ( return (
<span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "startDate",
header: t("start-date"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("startDate")}</span>
),
},
{
accessorKey: "endDate",
header: t("end-date"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("endDate")}</span>
),
},
{
accessorKey: "time",
header: t("time"),
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { startTime, endTime } = row.original;
return (
<span className="whitespace-nowrap">
{startTime || "N/A"} - {endTime || "N/A"}
</span>
);
},
},
{
accessorKey: "address",
header: t("address"),
cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const address: string = row.getValue("address");
return (
<span className="whitespace-nowrap">
{address.length > 50 ? `${address.slice(0, 40)}...` : address}
</span>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
// Gunakan `statusName` untuk pencocokan
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge
className={cn("rounded-full px-5 whitespace-nowrap", statusStyles)}
>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
},
},
{
accessorKey: "speaker",
header: t("speaker"),
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { speakerTitle, speakerName } = row.original;
return (
<span className="whitespace-nowrap">
{speakerTitle || ""} {speakerName || ""}
</span>
);
},
},
{
accessorKey: "uploaderName",
header: t("source"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("uploaderName")}
</span> </span>
); ),
}, },
},
{
accessorKey: "startDate",
header: "Start Date ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("startDate")}</span>
),
},
{
accessorKey: "endDate",
header: "End Date",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("endDate")}</span>
),
},
{
accessorKey: "time",
header: "Time",
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { startTime, endTime } = row.original;
return (
<span className="whitespace-nowrap">
{startTime || "N/A"} - {endTime || "N/A"}
</span>
);
},
},
{
accessorKey: "address",
header: "Address",
cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const address: string = row.getValue("address");
return (
<span className="whitespace-nowrap">
{address.length > 50 ? `${address.slice(0, 40)}...` : address}
</span>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API {
const status = row.getValue("statusName") as string; id: "actions",
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil accessorKey: "action",
header: t("action"),
// Gunakan `statusName` untuk pencocokan enableHiding: false,
const statusStyles = cell: ({ row }) => {
statusColors[statusName] || "bg-gray-100 text-gray-600"; return (
<DropdownMenu>
return ( <DropdownMenuTrigger asChild>
<Badge <Button
className={cn("rounded-full px-5 whitespace-nowrap", statusStyles)} size="icon"
> className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
{status} {/* Tetap tampilkan nilai asli */} >
</Badge> <span className="sr-only">Open menu</span>
); <MoreVertical className="h-4 w-4 text-default-800" />
}, </Button>
}, </DropdownMenuTrigger>
{ <DropdownMenuContent className="p-0" align="end">
accessorKey: "speaker", <Link
header: "Disampaikan oleh", href={`/contributor/schedule/event/detail/${row.original.id}`}
cell: ({ row }: { row: { original: any } }) => { >
console.log("Row Original Data:", row.original); <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
const { speakerTitle, speakerName } = row.original; <Eye className="w-4 h-4 me-1.5" />
return ( View
<span className="whitespace-nowrap"> </DropdownMenuItem>
{speakerTitle || ""} {speakerName || ""} </Link>
</span> <Link
); href={`/contributor/schedule/event/update/${row.original.id}`}
}, >
}, <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
{ <SquarePen className="w-4 h-4 me-1.5" />
accessorKey: "uploaderName", Edit
header: "Sumber ", </DropdownMenuItem>
cell: ({ row }) => ( </Link>
<span className="whitespace-nowrap">{row.getValue("uploaderName")}</span> <DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
), <Trash2 className="w-4 h-4 me-1.5" />
}, Delete
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/schedule/event/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
<Link </DropdownMenu>
href={`/contributor/schedule/event/update/${row.original.id}`} );
> },
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
]; return columns;
};
export default columns; export default useTableColumns;

View File

@ -33,6 +33,7 @@ import { useTranslations } from "next-intl";
import { CardHeader, CardTitle } from "@/components/ui/card"; import { CardHeader, CardTitle } from "@/components/ui/card";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import useTableColumns from "./columns";
const EventTable = () => { const EventTable = () => {
const router = useRouter(); const router = useRouter();
@ -55,7 +56,7 @@ const EventTable = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -12,163 +12,172 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
},
{ {
accessorKey: "title", accessorKey: "title",
header: "Title", header: t("title"),
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const title: string = row.getValue("title"); const title: string = row.getValue("title");
return ( return (
<span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "startDate",
header: t("start-date"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("startDate")}</span>
),
},
{
accessorKey: "endDate",
header: t("end-date"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("endDate")}</span>
),
},
{
accessorKey: "time",
header: t("time"),
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { startTime, endTime } = row.original;
return (
<span className="whitespace-nowrap">
{startTime || "N/A"} - {endTime || "N/A"}
</span>
);
},
},
{
accessorKey: "address",
header: t("address"),
cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const address: string = row.getValue("address");
return (
<span className="whitespace-nowrap">
{address.length > 50 ? `${address.slice(0, 40)}...` : address}
</span>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
// Gunakan `statusName` untuk pencocokan
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge
className={cn("rounded-full px-5 whitespace-nowrap", statusStyles)}
>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
},
},
{
accessorKey: "speaker",
header: t("speaker"),
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { speakerTitle, speakerName } = row.original;
return (
<span className="whitespace-nowrap">
{speakerTitle || ""} {speakerName || ""}
</span>
);
},
},
{
accessorKey: "uploaderName",
header: t("source"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("uploaderName")}
</span> </span>
); ),
}, },
},
{
accessorKey: "startDate",
header: "Start Date ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("startDate")}</span>
),
},
{
accessorKey: "endDate",
header: "End Date",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("endDate")}</span>
),
},
{
accessorKey: "time",
header: "Time",
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { startTime, endTime } = row.original;
return (
<span className="whitespace-nowrap">
{startTime || "N/A"} - {endTime || "N/A"}
</span>
);
},
},
{
accessorKey: "address",
header: "Address",
cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const address: string = row.getValue("address");
return (
<span className="whitespace-nowrap">
{address.length > 50 ? `${address.slice(0, 40)}...` : address}
</span>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API {
const status = row.getValue("statusName") as string; id: "actions",
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil accessorKey: "action",
header: t("action"),
// Gunakan `statusName` untuk pencocokan enableHiding: false,
const statusStyles = cell: ({ row }) => {
statusColors[statusName] || "bg-gray-100 text-gray-600"; return (
<DropdownMenu>
return ( <DropdownMenuTrigger asChild>
<Badge <Button
className={cn("rounded-full px-5 whitespace-nowrap", statusStyles)} size="icon"
> className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
{status} {/* Tetap tampilkan nilai asli */} >
</Badge> <span className="sr-only">Open menu</span>
); <MoreVertical className="h-4 w-4 text-default-800" />
}, </Button>
}, </DropdownMenuTrigger>
{ <DropdownMenuContent className="p-0" align="end">
accessorKey: "speaker", <Link
header: "Disampaikan oleh", href={`/contributor/schedule/press-conference/detail/${row.original.id}`}
cell: ({ row }: { row: { original: any } }) => { >
console.log("Row Original Data:", row.original); <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
const { speakerTitle, speakerName } = row.original; <Eye className="w-4 h-4 me-1.5" />
return ( Detail
<span className="whitespace-nowrap"> </DropdownMenuItem>
{speakerTitle || ""} {speakerName || ""} </Link>
</span> <Link
); href={`/contributor/schedule/press-conference/update/${row.original.id}`}
}, >
}, <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
{ <SquarePen className="w-4 h-4 me-1.5" />
accessorKey: "uploaderName", Edit
header: "Sumber ", </DropdownMenuItem>
cell: ({ row }) => ( </Link>
<span className="whitespace-nowrap">{row.getValue("uploaderName")}</span> <DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
), <Trash2 className="w-4 h-4 me-1.5" />
}, Delete
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/schedule/press-conference/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
Detail
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
<Link </DropdownMenu>
href={`/contributor/schedule/press-conference/update/${row.original.id}`} );
> },
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -46,6 +46,7 @@ import { paginationSchedule } from "@/service/schedule/schedule";
import { CardHeader, CardTitle } from "@/components/ui/card"; import { CardHeader, CardTitle } from "@/components/ui/card";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import useTableColumns from "./columns";
const PressConferenceTable = () => { const PressConferenceTable = () => {
const router = useRouter(); const router = useRouter();
@ -68,7 +69,7 @@ const PressConferenceTable = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -12,163 +12,172 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
), },
},
{ {
accessorKey: "title", accessorKey: "title",
header: "Title", header: t("title"),
cell: ({ row }: { row: { getValue: (key: string) => string } }) => { cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const title: string = row.getValue("title"); const title: string = row.getValue("title");
return ( return (
<span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title}
</span>
);
},
},
{
accessorKey: "startDate",
header: t("start-date"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("startDate")}</span>
),
},
{
accessorKey: "endDate",
header: t("end-date"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("endDate")}</span>
),
},
{
accessorKey: "time",
header: t("time"),
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { startTime, endTime } = row.original;
return (
<span className="whitespace-nowrap">
{startTime || "N/A"} - {endTime || "N/A"}
</span>
);
},
},
{
accessorKey: "address",
header: t("address"),
cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const address: string = row.getValue("address");
return (
<span className="whitespace-nowrap">
{address.length > 50 ? `${address.slice(0, 40)}...` : address}
</span>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil
// Gunakan `statusName` untuk pencocokan
const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600";
return (
<Badge
className={cn("rounded-full px-5 whitespace-nowrap", statusStyles)}
>
{status} {/* Tetap tampilkan nilai asli */}
</Badge>
);
},
},
{
accessorKey: "speaker",
header: t("speaker"),
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { speakerTitle, speakerName } = row.original;
return (
<span className="whitespace-nowrap">
{speakerTitle || ""} {speakerName || ""}
</span>
);
},
},
{
accessorKey: "uploaderName",
header: t("source"),
cell: ({ row }) => (
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{title.length > 50 ? `${title.slice(0, 30)}...` : title} {row.getValue("uploaderName")}
</span> </span>
); ),
}, },
},
{
accessorKey: "startDate",
header: "Start Date ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("startDate")}</span>
),
},
{
accessorKey: "endDate",
header: "End Date",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("endDate")}</span>
),
},
{
accessorKey: "time",
header: "Time",
cell: ({ row }: { row: { original: any } }) => {
console.log("Row Original Data:", row.original);
const { startTime, endTime } = row.original;
return (
<span className="whitespace-nowrap">
{startTime || "N/A"} - {endTime || "N/A"}
</span>
);
},
},
{
accessorKey: "address",
header: "Address",
cell: ({ row }: { row: { getValue: (key: string) => string } }) => {
const address: string = row.getValue("address");
return (
<span className="whitespace-nowrap">
{address.length > 50 ? `${address.slice(0, 40)}...` : address}
</span>
);
},
},
{
accessorKey: "statusName",
header: "Status",
cell: ({ row }) => {
const statusColors: Record<string, string> = {
diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600",
};
// Mengambil `statusName` dari data API {
const status = row.getValue("statusName") as string; id: "actions",
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil accessorKey: "action",
header: t("action"),
// Gunakan `statusName` untuk pencocokan enableHiding: false,
const statusStyles = cell: ({ row }) => {
statusColors[statusName] || "bg-gray-100 text-gray-600"; return (
<DropdownMenu>
return ( <DropdownMenuTrigger asChild>
<Badge <Button
className={cn("rounded-full px-5 whitespace-nowrap", statusStyles)} size="icon"
> className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
{status} {/* Tetap tampilkan nilai asli */} >
</Badge> <span className="sr-only">Open menu</span>
); <MoreVertical className="h-4 w-4 text-default-800" />
}, </Button>
}, </DropdownMenuTrigger>
{ <DropdownMenuContent className="p-0" align="end">
accessorKey: "speaker", <Link
header: "Disampaikan oleh", href={`/contributor/schedule/press-release/detail/${row.original.id}`}
cell: ({ row }: { row: { original: any } }) => { >
console.log("Row Original Data:", row.original); <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
const { speakerTitle, speakerName } = row.original; <Eye className="w-4 h-4 me-1.5" />
return ( View
<span className="whitespace-nowrap"> </DropdownMenuItem>
{speakerTitle || ""} {speakerName || ""} </Link>
</span> <Link
); href={`/contributor/schedule/press-release/update/${row.original.id}`}
}, >
}, <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
{ <SquarePen className="w-4 h-4 me-1.5" />
accessorKey: "uploaderName", Edit
header: "Sumber ", </DropdownMenuItem>
cell: ({ row }) => ( </Link>
<span className="whitespace-nowrap">{row.getValue("uploaderName")}</span> <DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
), <Trash2 className="w-4 h-4 me-1.5" />
}, Delete
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/schedule/press-release/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
<Link </DropdownMenu>
href={`/contributor/schedule/press-release/update/${row.original.id}`} );
> },
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -47,6 +47,7 @@ import { paginationSchedule } from "@/service/schedule/schedule";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { CardHeader, CardTitle } from "@/components/ui/card"; import { CardHeader, CardTitle } from "@/components/ui/card";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import useTableColumns from "./columns";
const PressReleaseTable = () => { const PressReleaseTable = () => {
const router = useRouter(); const router = useRouter();
@ -69,7 +70,7 @@ const PressReleaseTable = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -20,187 +20,193 @@ import { deleteTask } from "@/service/task";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no", const columns: ColumnDef<any>[] = [
header: "No", {
cell: ({ row }) => <span>{row.getValue("no")}</span>, accessorKey: "no",
}, header: t("no"),
{ cell: ({ row }) => <span>{row.getValue("no")}</span>,
accessorKey: "title",
header: "Title",
cell: ({ row }) => (
<div>
<span>{row.getValue("title")}</span>
{row.original.isForward && (
<Button
variant={"outline"}
color="primary"
size="sm"
className="ml-3 rounded-xl"
>
Forward
</Button>
)}
</div>
),
},
{
accessorKey: "uniqueCode",
header: "Code",
cell: ({ row }) => <span>{row.getValue("uniqueCode")}</span>,
},
{
accessorKey: "assignmentMainType",
header: "Type Task",
cell: ({ row }) => {
const type = row.getValue("assignmentMainType") as { name: string };
return <span>{type?.name}</span>;
}, },
}, {
{ accessorKey: "title",
accessorKey: "assignmentType", header: t("title"),
header: "Category Task", cell: ({ row }) => (
cell: ({ row }) => { <div>
const type = row.getValue("assignmentType") as { name: string }; <span>{row.getValue("title")}</span>
return <span>{type?.name}</span>; {row.original.isForward && (
},
},
{
accessorKey: "createdAt",
header: "Upload Date ",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => {
const isActive = row.original.isActive;
const isDone = row.original.isDone;
let statusText = "";
if (isDone) {
statusText = "Selesai";
} else if (isActive) {
statusText = "Aktif";
} else {
statusText = "Nonaktif";
}
const statusColors: Record<string, string> = {
Aktif: "bg-primary/20 text-primary",
Selesai: "bg-success/20 text-success",
Nonaktif: "bg-gray-200 text-gray-500",
};
const statusStyles = statusColors[statusText] || "default";
return (
<Badge className={cn("rounded-full px-5", statusStyles)}>
{statusText}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const MySwal = withReactContent(Swal);
async function deleteProcess(id: any) {
loading();
const resDelete = await deleteTask(id);
if (resDelete?.error) {
error(resDelete.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const TaskDelete = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
deleteProcess(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button <Button
size="icon" variant={"outline"}
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent" color="primary"
size="sm"
className="ml-3 rounded-xl"
> >
<span className="sr-only">Open menu</span> Forward
<MoreVertical className="h-4 w-4 text-default-800" />
</Button> </Button>
</DropdownMenuTrigger> )}
<DropdownMenuContent className="p-0" align="end"> </div>
<Link href={`/contributor/task/detail/${row.original.id}`}> ),
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/task/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => TaskDelete(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
},
];
export default columns; {
accessorKey: "uniqueCode",
header: t("code"),
cell: ({ row }) => <span>{row.getValue("uniqueCode")}</span>,
},
{
accessorKey: "assignmentMainType",
header: t("type-task"),
cell: ({ row }) => {
const type = row.getValue("assignmentMainType") as { name: string };
return <span>{type?.name}</span>;
},
},
{
accessorKey: "assignmentType",
header: t("category-task"),
cell: ({ row }) => {
const type = row.getValue("assignmentType") as { name: string };
return <span>{type?.name}</span>;
},
},
{
accessorKey: "createdAt",
header: t("upload-date"),
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => {
const isActive = row.original.isActive;
const isDone = row.original.isDone;
let statusText = "";
if (isDone) {
statusText = "Selesai";
} else if (isActive) {
statusText = "Aktif";
} else {
statusText = "Nonaktif";
}
const statusColors: Record<string, string> = {
Aktif: "bg-primary/20 text-primary",
Selesai: "bg-success/20 text-success",
Nonaktif: "bg-gray-200 text-gray-500",
};
const statusStyles = statusColors[statusText] || "default";
return (
<Badge className={cn("rounded-full px-5", statusStyles)}>
{statusText}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const MySwal = withReactContent(Swal);
async function deleteProcess(id: any) {
loading();
const resDelete = await deleteTask(id);
if (resDelete?.error) {
error(resDelete.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const TaskDelete = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
deleteProcess(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/task/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/task/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => TaskDelete(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -56,6 +56,7 @@ import { listTask } from "@/service/task";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { format } from "date-fns"; import { format } from "date-fns";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import useTableColumns from "./columns";
const TaskTable = () => { const TaskTable = () => {
const router = useRouter(); const router = useRouter();
@ -83,7 +84,7 @@ const TaskTable = () => {
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [isSpecificAttention, setIsSpecificAttention] = React.useState(true); const [isSpecificAttention, setIsSpecificAttention] = React.useState(true);
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -181,16 +181,16 @@ export default function ExecutiveDashboard() {
const view2 = const view2 =
levelName == "MABES POLRI" levelName == "MABES POLRI"
? isInternational[1] ? isInternational[1]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-publisher?" ? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-publisher?" : "views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-published-produksi-executive?"
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-publisher-polda?provinsi-polda=${poldaState}&`; : `views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-konten-publisher-polda-executive?provinsi-polda=${poldaState}&`;
const view3 = const view3 =
levelName == "MABES POLRI" levelName == "MABES POLRI"
? isInternational[2] ? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?" ? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-waktu-akses-pengguna?" : "views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-waktu-akses-pengguna-executive?"
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-waktu-akses-pengguna-polda?provinsi-polda=${poldaState}&`; : `views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-waktu-akses-pengguna-polda-executive?provinsi-polda=${poldaState}&`;
const view4 = const view4 =
levelName == "MABES POLRI" levelName == "MABES POLRI"
@ -212,6 +212,21 @@ export default function ExecutiveDashboard() {
async function initState() { async function initState() {
const response1 = await generateTicket(); const response1 = await generateTicket();
setTicket1(response1?.data?.data); setTicket1(response1?.data?.data);
const response2 = await generateTicket();
setTicket2(response2?.data?.data);
const response3 = await generateTicket();
setTicket3(response3?.data?.data);
const response4 = await generateTicket();
setTicket4(response4?.data?.data);
const response5 = await generateTicket();
setTicket5(response5?.data?.data);
const response6 = await generateTicket();
setTicket6(response6?.data?.data);
} }
initState(); initState();
@ -235,35 +250,6 @@ export default function ExecutiveDashboard() {
return ( return (
<div> <div>
<SiteBreadcrumb /> <SiteBreadcrumb />
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Download Report</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Download Report</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="w-full">
<Label>Date</Label>
<Input
type="date"
// value={dateFilter}
// onChange={(e) => setDateFilter(e.target.value)}
className="w-full"
/>
</div>
</div>
<DialogFooter>
<Button
type="submit"
// onClick={downloadReport}
>
Download
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<div className="mt-3 flex flex-row gap-3 justify-center"> <div className="mt-3 flex flex-row gap-3 justify-center">
<Card className="rounded-sm w-4/12 p-3"> <Card className="rounded-sm w-4/12 p-3">
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
@ -298,7 +284,7 @@ export default function ExecutiveDashboard() {
<LucideBoxSelect /> <LucideBoxSelect />
</div> </div>
<div className="my-5"> <div className="my-5">
{ticket1 == "" ? ( {ticket2 == "" ? (
<iframe <iframe
src={`${baseUrl + view4 + param}`} src={`${baseUrl + view4 + param}`}
width="100%" width="100%"
@ -307,7 +293,7 @@ export default function ExecutiveDashboard() {
/> />
) : ( ) : (
<iframe <iframe
src={`${`${url + ticket1}/${view4}${param}`}`} src={`${`${url + ticket2}/${view4}${param}`}`}
width="100%" width="100%"
height="750" height="750"
frameBorder="0" frameBorder="0"
@ -323,7 +309,7 @@ export default function ExecutiveDashboard() {
<LucideBoxSelect /> <LucideBoxSelect />
</div> </div>
<div className="my-5"> <div className="my-5">
{ticket1 == "" ? ( {ticket3 == "" ? (
<iframe <iframe
src={`${baseUrl + view5 + param}`} src={`${baseUrl + view5 + param}`}
width="100%" width="100%"
@ -332,7 +318,7 @@ export default function ExecutiveDashboard() {
/> />
) : ( ) : (
<iframe <iframe
src={`${`${url + ticket1}/${view5}${param}`}`} src={`${`${url + ticket3}/${view5}${param}`}`}
width="100%" width="100%"
height="750" height="750"
frameBorder="0" frameBorder="0"
@ -342,24 +328,24 @@ export default function ExecutiveDashboard() {
</Card> </Card>
</div> </div>
<div className="w-full mt-3"> <div className="w-full mt-3">
<Card className="rounded-sm p-3 h-[850px]"> <Card className="rounded-sm p-3 h-[750px]">
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<p className="text-base font-semibold">Konten Paling Populer</p> <p className="text-base font-semibold">Konten Paling Populer</p>
<LucideBoxSelect /> <LucideBoxSelect />
</div> </div>
<div className="my-5"> <div className="my-5">
{ticket2 == "" ? ( {ticket4 == "" ? (
<iframe <iframe
src={`${baseUrl + view2 + param}`} src={`${baseUrl + view2 + param}`}
width="100%" width="100%"
height="750" height="650"
frameBorder="0" frameBorder="0"
/> />
) : ( ) : (
<iframe <iframe
src={`${`${url + ticket2}/${view2}${param}`}`} src={`${`${url + ticket4}/${view2}${param}`}`}
width="100%" width="100%"
height="750" height="650"
frameBorder="0" frameBorder="0"
/> />
)} )}
@ -367,7 +353,7 @@ export default function ExecutiveDashboard() {
</Card> </Card>
</div> </div>
<div className="w-full mt-3"> <div className="w-full mt-3">
<Card className="rounded-sm p-3 h-[850px]"> <Card className="rounded-sm p-3 h-[750px]">
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<p className="text-base font-semibold"> <p className="text-base font-semibold">
Heatmap Konten Dengan Interaksi Heatmap Konten Dengan Interaksi
@ -378,18 +364,18 @@ export default function ExecutiveDashboard() {
<LucideBoxSelect /> <LucideBoxSelect />
</div> </div>
<div className="my-5"> <div className="my-5">
{ticket3 == "" ? ( {ticket5 == "" ? (
<iframe <iframe
src={`${baseUrl + view3 + param}`} src={`${baseUrl + view3 + param}`}
width="100%" width="100%"
height="750" height="600"
frameBorder="0" frameBorder="0"
/> />
) : ( ) : (
<iframe <iframe
src={`${`${url + ticket3}/${view3}${param}`}`} src={`${`${url + ticket5}/${view3}${param}`}`}
width="100%" width="100%"
height="750" height="600"
frameBorder="0" frameBorder="0"
/> />
)} )}
@ -397,7 +383,7 @@ export default function ExecutiveDashboard() {
</Card> </Card>
</div> </div>
<div className="w-full mt-3"> <div className="w-full mt-3">
<Card className="rounded-sm p-3 h-[300px]"> <Card className="rounded-sm p-3 h-auto">
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<p className="text-base font-semibold">Emergency Issue</p> <p className="text-base font-semibold">Emergency Issue</p>
<LucideBoxSelect /> <LucideBoxSelect />
@ -405,7 +391,7 @@ export default function ExecutiveDashboard() {
<div className="flex flex-col"> <div className="flex flex-col">
<div className="my-5"> <div className="my-5">
{ticket1 == "" ? ( {ticket6 == "" ? (
<iframe <iframe
src={`${baseUrl + view1 + param}`} src={`${baseUrl + view1 + param}`}
width="100%" width="100%"
@ -414,7 +400,7 @@ export default function ExecutiveDashboard() {
/> />
) : ( ) : (
<iframe <iframe
src={`${`${url + ticket1}/${view1}${param}`}`} src={`${`${url + ticket6}/${view1}${param}`}`}
width="100%" width="100%"
height="750" height="750"
frameBorder="0" frameBorder="0"

View File

@ -63,6 +63,7 @@ import {
getTicketingEscalationPagination, getTicketingEscalationPagination,
listTicketingInternal, listTicketingInternal,
} from "@/service/communication/communication"; } from "@/service/communication/communication";
import useTableColumns from "./columns";
const CollaborationTable = () => { const CollaborationTable = () => {
const router = useRouter(); const router = useRouter();
@ -88,7 +89,7 @@ const CollaborationTable = () => {
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -13,102 +13,113 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link, useRouter } from "@/i18n/routing"; import { Link, useRouter } from "@/i18n/routing";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => <span> {row.getValue("no")}</span>, {
}, accessorKey: "no",
{ header: t("no"),
accessorKey: "title", cell: ({ row }) => <span> {row.getValue("no")}</span>,
header: "Pertanyaan",
cell: ({ row }) => (
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "commentFromUserName",
header: "CreateBy",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
),
},
{
accessorKey: "Type",
header: "Channel",
cell: ({ row }) => {
const type = row.original.type;
return <span className="normal-case">{type?.name || "N/A"}</span>;
}, },
}, {
{ accessorKey: "title",
accessorKey: "createdAt", header: t("question"),
header: "Waktu", cell: ({ row }) => (
cell: ({ row }) => { <span className="normal-case"> {row.getValue("title")}</span>
const createdAt = row.getValue("createdAt") as ),
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
}, },
}, {
{ accessorKey: "commentFromUserName",
accessorKey: "isActive", header: t("sender"),
header: "Status", cell: ({ row }) => (
cell: ({ row }) => { <span className="normal-case">
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive {row.getValue("commentFromUserName")}
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive </span>
const statusStyles = isActive ),
? "bg-green-100 text-green-600" // Gaya untuk "Open"
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
return (
<Badge className={`rounded-full px-5 ${statusStyles}`}>{status}</Badge>
);
}, },
}, {
accessorKey: "Type",
{ header: t("type"),
id: "actions", cell: ({ row }) => {
accessorKey: "action", const type = row.original.type;
header: "Actions", return <span className="normal-case">{type?.name || "N/A"}</span>;
enableHiding: false, },
cell: ({ row }) => {
const router = useRouter();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<DropdownMenuItem
onClick={() =>
router.push(
`/shared/communication/collaboration/detail/${row.original.id}`
)
}
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center"
>
<Eye className="w-4 h-4 me-1.5" />
Detail
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, {
]; accessorKey: "createdAt",
header: t("time"),
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
export default columns; const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "isActive",
header: "Status",
cell: ({ row }) => {
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive
const statusStyles = isActive
? "bg-green-100 text-green-600" // Gaya untuk "Open"
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
return (
<Badge className={`rounded-full px-5 ${statusStyles}`}>
{status}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<DropdownMenuItem
onClick={() =>
router.push(
`/shared/communication/collaboration/detail/${row.original.id}`
)
}
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center"
>
<Eye className="w-4 h-4 me-1.5" />
Detail
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -13,98 +13,108 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => <span> {row.getValue("no")}</span>, {
}, accessorKey: "no",
{ header: t("no"),
accessorKey: "title", cell: ({ row }) => <span> {row.getValue("no")}</span>,
header: "Pertanyaan",
cell: ({ row }) => (
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "commentFromUserName",
header: "Penerima",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
),
},
{
accessorKey: "type",
header: "Penerima",
cell: ({ row }) => {
const type = row.original.type; // Akses properti category
return <span className="normal-case">{type?.name || "N/A"}</span>;
}, },
}, {
{ accessorKey: "title",
accessorKey: "createdAt", header: t("question"),
header: "Waktu", cell: ({ row }) => (
cell: ({ row }) => { <span className="normal-case"> {row.getValue("title")}</span>
const createdAt = row.getValue("createdAt") as ),
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
}, },
}, {
{ accessorKey: "commentFromUserName",
accessorKey: "isActive", header: t("sender"),
header: "Status", cell: ({ row }) => (
cell: ({ row }) => { <span className="normal-case">
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive {row.getValue("commentFromUserName")}
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive </span>
const statusStyles = isActive ),
? "bg-green-100 text-green-600" // Gaya untuk "Open"
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
return (
<Badge className={`rounded-full px-5 ${statusStyles}`}>{status}</Badge>
);
}, },
}, {
accessorKey: "type",
{ header: t("type"),
id: "actions", cell: ({ row }) => {
accessorKey: "action", const type = row.original.type; // Akses properti category
header: "Actions", return <span className="normal-case">{type?.name || "N/A"}</span>;
enableHiding: false, },
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/shared/communication/escalation/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, {
]; accessorKey: "createdAt",
header: t("time"),
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
export default columns; const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "isActive",
header: "Status",
cell: ({ row }) => {
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive
const statusStyles = isActive
? "bg-green-100 text-green-600" // Gaya untuk "Open"
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
return (
<Badge className={`rounded-full px-5 ${statusStyles}`}>
{status}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/shared/communication/escalation/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
return columns;
};
export default useTableColumns;

View File

@ -62,6 +62,7 @@ import {
getTicketingEscalationPagination, getTicketingEscalationPagination,
listTicketingInternal, listTicketingInternal,
} from "@/service/communication/communication"; } from "@/service/communication/communication";
import useTableColumns from "./columns";
const EscalationTable = () => { const EscalationTable = () => {
const router = useRouter(); const router = useRouter();
@ -87,7 +88,7 @@ const EscalationTable = () => {
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -13,97 +13,104 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No",
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Pertanyaan",
cell: ({ row }) => (
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "createdBy",
header: "Pengirim",
cell: ({ row }) => {
const createdBy = row.original.createdBy; // Akses properti category
return (
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
);
},
},
{
accessorKey: "sendTo",
header: "Penerima",
cell: ({ row }) => {
const sendTo = row.original.sendTo; // Akses properti category
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate = const columns: ColumnDef<any>[] = [
createdAt && !isNaN(new Date(createdAt).getTime()) {
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") accessorKey: "no",
: "-"; header: t("no"),
return <span className="whitespace-nowrap">{formattedDate}</span>; cell: ({ row }) => <span> {row.getValue("no")}</span>,
}, },
}, {
{ accessorKey: "title",
id: "actions", header: t("question"),
accessorKey: "action", cell: ({ row }) => (
header: "Actions", <span className="normal-case"> {row.getValue("title")}</span>
enableHiding: false, ),
cell: ({ row }) => { },
return ( {
<DropdownMenu> accessorKey: "createdBy",
<DropdownMenuTrigger asChild> header: t("sender"),
<Button cell: ({ row }) => {
size="icon" const createdBy = row.original.createdBy; // Akses properti category
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent" return (
> <span className="normal-case">{createdBy?.fullname || "N/A"}</span>
<span className="sr-only">Open menu</span> );
<MoreVertical className="h-4 w-4 text-default-800" /> },
</Button> },
</DropdownMenuTrigger> {
<DropdownMenuContent className="p-0" align="end"> accessorKey: "sendTo",
<Link header: t("sendto"),
href={`/shared/communication/internal/detail/${row.original.id}`} cell: ({ row }) => {
> const sendTo = row.original.sendTo; // Akses properti category
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
<Eye className="w-4 h-4 me-1.5" /> },
View },
{
accessorKey: "createdAt",
header: t("time"),
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/shared/communication/internal/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link
href={`/shared/communication/internal/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem> </DropdownMenuItem>
</Link> </DropdownMenuContent>
<Link </DropdownMenu>
href={`/shared/communication/internal/update/${row.original.id}`} );
> },
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -61,6 +61,7 @@ import {
} from "@/service/content/content"; } from "@/service/content/content";
import { listTicketingInternal } from "@/service/communication/communication"; import { listTicketingInternal } from "@/service/communication/communication";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import useTableColumns from "./columns";
const InternalTable = () => { const InternalTable = () => {
const router = useRouter(); const router = useRouter();
@ -88,7 +89,7 @@ const InternalTable = () => {
const userLevelId = getCookiesDecrypt("ulie"); const userLevelId = getCookiesDecrypt("ulie");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -18,198 +18,206 @@ import Swal from "sweetalert2";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import { deleteMedia } from "@/service/content/content"; import { deleteMedia } from "@/service/content/content";
import { publishContest } from "@/service/contest/contest"; import { publishContest } from "@/service/contest/contest";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [ const useTableColumns = () => {
{ const t = useTranslations("Table"); // Panggil di dalam hook
accessorKey: "no",
header: "No", const columns: ColumnDef<any>[] = [
cell: ({ row }) => ( {
<div className="flex items-center gap-5"> accessorKey: "no",
<div className="flex-1 text-start"> header: t("no"),
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1"> cell: ({ row }) => (
{row.getValue("no")} <div className="flex items-center gap-5">
</h4> <div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div> </div>
</div> ),
),
},
{
accessorKey: "hastagCode",
header: "Kode",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4
className="text-sm font-bold
text-default-600 whitespace-nowrap mb-1"
>
{row.getValue("hastagCode")}
</h4>
</div>
</div>
),
},
{
accessorKey: "theme",
header: "Judul",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("theme")}
</h4>
</div>
</div>
),
},
{
accessorKey: "duration",
header: "Durasi ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("duration")}</span>
),
},
{
accessorKey: "targetOutput",
header: "Target Output ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("targetOutput")}</span>
),
},
{
accessorKey: "targetParticipantTopLevel",
header: "Target Participant ",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("targetParticipantTopLevel")}
</span>
),
},
{
accessorKey: "isPublishForAll", // Bisa menggunakan ini untuk membaca default data
header: "Status",
cell: ({
row,
}: {
row: {
original: { isPublishForAll?: boolean; isPublishForMabes?: boolean };
};
}) => {
const userRoleId: number = Number(getCookiesDecrypt("urie"));
const userLevelNumber: number = Number(getCookiesDecrypt("ulne"));
// Mengambil data dari row.original agar lebih aman
const isPublishForAll: boolean = Boolean(row.original.isPublishForAll);
const isPublishForMabes: boolean = Boolean(
row.original.isPublishForMabes
);
// Logika status berdasarkan role dan data dari API
const isPending: boolean =
(userRoleId === 3 && userLevelNumber === 1 && !isPublishForAll) || // Role 3 dengan level 1 hanya publish jika isPublishForAll true
((userRoleId === 11 || userRoleId === 12) && !isPublishForMabes); // Role 11 dan 12 hanya publish jika isPublishForMabes true
return (
<Badge
className={`whitespace-nowrap px-2 py-1 rounded-full ${
isPending
? "bg-orange-100 text-orange-600" // Warna kuning untuk "Pending"
: "bg-green-100 text-green-600" // Warna hijau untuk "Publish"
}`}
>
{isPending ? "Pending" : "Publish"}
</Badge>
);
}, },
}, {
accessorKey: "hastagCode",
{ header: t("code"),
id: "actions", cell: ({ row }) => (
accessorKey: "action", <div className="flex items-center gap-5">
header: "Actions", <div className="flex-1 text-start">
enableHiding: false, <h4
cell: ({ row }) => { className="text-sm font-bold
const MySwal = withReactContent(Swal); text-default-600 whitespace-nowrap mb-1"
const userRoleId = Number(getCookiesDecrypt("urie"));
const userLevelId = Number(getCookiesDecrypt("ulie"));
const userLevelNumber = Number(getCookiesDecrypt("ulne"));
async function doPublish(id: any) {
// loading();
// const data = {
// id,
// };
const response = await publishContest(id);
if (response?.error) {
error(response.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handlePublishContest = (id: any) => {
MySwal.fire({
title: "Apakah anda ingin publish Lomba?",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Ya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doPublish(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
> >
<span className="sr-only">Open menu</span> {row.getValue("hastagCode")}
<MoreVertical className="h-4 w-4 text-default-800" /> </h4>
</Button> </div>
</DropdownMenuTrigger> </div>
<DropdownMenuContent className="p-0" align="end"> ),
{((userRoleId == 11 || userRoleId == 12) && },
row?.original?.isPublishForMabes != true) || {
(userRoleId == 3 && accessorKey: "theme",
userLevelNumber == 1 && header: t("title"),
row?.original?.isPublishForAll != true) ? ( cell: ({ row }) => (
<DropdownMenuItem <div className="flex items-center gap-5">
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none" <div className="flex-1 text-start">
onClick={() => handlePublishContest(row.original.id)} <h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("theme")}
</h4>
</div>
</div>
),
},
{
accessorKey: "duration",
header: t("duration"),
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("duration")}</span>
),
},
{
accessorKey: "targetOutput",
header: t("target-output"),
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("targetOutput")}
</span>
),
},
{
accessorKey: "targetParticipantTopLevel",
header: t("target-participant"),
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("targetParticipantTopLevel")}
</span>
),
},
{
accessorKey: "isPublishForAll", // Bisa menggunakan ini untuk membaca default data
header: "Status",
cell: ({
row,
}: {
row: {
original: { isPublishForAll?: boolean; isPublishForMabes?: boolean };
};
}) => {
const userRoleId: number = Number(getCookiesDecrypt("urie"));
const userLevelNumber: number = Number(getCookiesDecrypt("ulne"));
const isPublishForAll: boolean = Boolean(row.original.isPublishForAll);
const isPublishForMabes: boolean = Boolean(
row.original.isPublishForMabes
);
const isPending: boolean =
(userRoleId === 3 && userLevelNumber === 1 && !isPublishForAll) ||
((userRoleId === 11 || userRoleId === 12) && !isPublishForMabes);
const isTerkirim: boolean = isPublishForMabes && !isPublishForAll;
return (
<Badge
className={`whitespace-nowrap px-2 py-1 rounded-full ${
isPending
? "bg-orange-100 text-orange-600" // Warna kuning untuk "Pending"
: isTerkirim
? "bg-blue-100 text-blue-600" // Warna biru untuk "Terkirim"
: "bg-green-100 text-green-600" // Warna hijau untuk "Publish"
}`}
>
{isPending ? "Pending" : isTerkirim ? "Terkirim" : "Publish"}
</Badge>
);
},
},
{
id: "actions",
accessorKey: "action",
header: t("action"),
enableHiding: false,
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
const userRoleId = Number(getCookiesDecrypt("urie"));
const userLevelId = Number(getCookiesDecrypt("ulie"));
const userLevelNumber = Number(getCookiesDecrypt("ulne"));
async function doPublish(id: any) {
// loading();
// const data = {
// id,
// };
const response = await publishContest(id);
if (response?.error) {
error(response.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handlePublishContest = (id: any) => {
MySwal.fire({
title: "Apakah anda ingin publish Lomba?",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Ya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doPublish(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
> >
<Upload className="w-4 h-4 me-1.5" /> <span className="sr-only">Open menu</span>
Publish <MoreVertical className="h-4 w-4 text-default-800" />
</DropdownMenuItem> </Button>
) : ( </DropdownMenuTrigger>
"" <DropdownMenuContent className="p-0" align="end">
)} {((userRoleId == 11 || userRoleId == 12) &&
<Link href={`/shared/contest/detail/${row.original.id}`}> row?.original?.isPublishForMabes != true) ||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> (userRoleId == 3 &&
<Eye className="w-4 h-4 me-1.5" /> userLevelNumber == 1 &&
View row?.original?.isPublishForAll != true) ? (
</DropdownMenuItem> <DropdownMenuItem
</Link> className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"
{/* <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> onClick={() => handlePublishContest(row.original.id)}
>
<Upload className="w-4 h-4 me-1.5" />
Publish
</DropdownMenuItem>
) : (
""
)}
<Link href={`/shared/contest/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
{/* <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" /> <SquarePen className="w-4 h-4 me-1.5" />
Edit Edit
</DropdownMenuItem> </DropdownMenuItem>
@ -217,11 +225,14 @@ const columns: ColumnDef<any>[] = [
<Trash2 className="w-4 h-4 me-1.5" /> <Trash2 className="w-4 h-4 me-1.5" />
Delete Delete
</DropdownMenuItem> */} </DropdownMenuItem> */}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
); );
},
}, },
}, ];
];
export default columns; return columns;
};
export default useTableColumns;

View File

@ -42,6 +42,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination"; import TablePagination from "@/components/table/table-pagination";
import columns from "./columns"; import columns from "./columns";
import { listContest } from "@/service/contest/contest"; import { listContest } from "@/service/contest/contest";
import useTableColumns from "./columns";
const TaskTable = () => { const TaskTable = () => {
const router = useRouter(); const router = useRouter();
@ -63,7 +64,7 @@ const TaskTable = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>(""); const [search, setSearch] = React.useState<string>("");
const columns = useTableColumns();
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { Card } from "@/components/ui/card";
import { import {
Carousel, Carousel,
CarouselContent, CarouselContent,
@ -30,14 +31,6 @@ const AudioSliderPage = () => {
initFetch(); initFetch();
}, [page, limit, search]); }, [page, limit, search]);
// useEffect(() => {
// if (audioData?.length > 0) {
// shuffleAndSetVideos();
// const interval = setInterval(shuffleAndSetVideos, 5000);
// return () => clearInterval(interval); // Cleanup interval on unmount
// }
// }, [audioData]);
const initFetch = async () => { const initFetch = async () => {
const response = await listCuratedContent(search, limit, page - 1, 4, "1"); const response = await listCuratedContent(search, limit, page - 1, 4, "1");
console.log(response); console.log(response);
@ -48,11 +41,6 @@ const AudioSliderPage = () => {
setDisplayAudio(contentData); setDisplayAudio(contentData);
}; };
// const shuffleAndSetVideos = () => {
// const shuffled = shuffleArray([...audioData]);
// setDisplayAudio(shuffled.slice(0, 3));
// };
const shuffleArray = (array: any[]) => { const shuffleArray = (array: any[]) => {
for (let i = array.length - 1; i > 0; i--) { for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); const j = Math.floor(Math.random() * (i + 1));
@ -62,46 +50,61 @@ const AudioSliderPage = () => {
}; };
return ( return (
<Carousel className="w-full pr-3"> <div className="w-full px-2">
<CarouselContent> {displayAudio.length > 0 && (
{Array.from({ length: 5 }).map((_, index) => ( <div>
<CarouselItem key={index}> <div className="flex justify-between items-center mb-2">
<div className="p-1 flex flex-row md:basis-1/2 lg:basis-1/2 gap-3"> <h2 className="text-xl font-semibold">Audio</h2>
{displayAudio?.map((audio: any) => ( <Link
<Link href={"/shared/curated-content/giat-routine/video/all"}
href={`/shared/curated-content//giat-routine/audio/detail/${audio.id}`} className="text-sm text-gray-500 hover:text-gray-700 flex items-center"
key={audio?.id} >
className="flex flex-col sm:flex-row items-center hover:scale-100 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full" Lihat Semua <Icon icon="lucide:arrow-right" className="ml-1" />
> </Link>
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-12 h-12"> </div>
<svg <Carousel className="w-full">
width="28" <CarouselContent>
height="28" {displayAudio.map((audio, index) => (
viewBox="0 0 32 34" <CarouselItem key={index} className="md:basis-1/3 lg:basis-1/3">
fill="null" <div className="p-2">
xmlns="http://www.w3.org/2000/svg" <Card className=" shadow-md rounded-lg overflow-hidden">
> <Link
<path href={`/shared/curated-content/giat-routine/audio/detail/${audio.id}`}
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z" >
fill="white" <div className="flex flex-row items-center gap-3">
/> <div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-12 h-12 mx-3 my-3">
</svg> <svg
</div> width="28"
height="28"
viewBox="0 0 32 34"
fill="null"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
fill="white"
/>
</svg>
</div>
<div className="flex flex-col flex-1"> <div className="flex flex-col flex-1">
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm"> <div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
{audio?.title} {audio?.title}
</div> </div>
</div>
</div>
</Link>
</Card>
</div> </div>
</Link> </CarouselItem>
))} ))}
</div> </CarouselContent>
</CarouselItem> <CarouselPrevious />
))} <CarouselNext />
</CarouselContent> </Carousel>
<CarouselPrevious /> </div>
<CarouselNext /> )}
</Carousel> </div>
); );
}; };

View File

@ -574,35 +574,36 @@ export default function DetailAudio() {
key={data.id} key={data.id}
className="flex items-center gap-3 mt-2" className="flex items-center gap-3 mt-2"
> >
{/* <img
className="object-cover w-20 h-20"
src={data.thumbnailUrl} // Assuming `thumbnailUrl` is the property that contains the URL for the thumbnail image
alt={`Thumbnail ${index}`}
/> */}
<Music className="object-cover w-32 h-32" /> <Music className="object-cover w-32 h-32" />
<div className="flex flex-wrap lg:flex-row gap-3 items-center"> <div className="flex flex-wrap lg:flex-row gap-3 items-center">
{/* Mabes Checkbox */} {/* Mabes Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "mabes"} // Automatically checks if placement matches checked={data.placements === "mabes"}
disabled // To reflect read-only behavior disabled
/> />
<span>Nasional</span> <span>Nasional</span>
</label> </label>
{/* Polda Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "polda"} // Automatically checks if placement matches checked={data.placements === "polda"}
disabled disabled
/> />
<span>Wilayah</span> <span>Wilayah</span>
</label> </label>
{/* International Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "international"} // Automatically checks if placement matches checked={data.placements === "satker"}
disabled
/>
<span>Satker</span>
</label>
<label className=" cursor-pointer flex items-center gap-2">
<Checkbox
checked={data.placements === "international"}
disabled disabled
/> />
<span>International</span> <span>International</span>

View File

@ -580,28 +580,33 @@ export default function DetailDocument() {
alt={data.fileName} alt={data.fileName}
/> />
<div className="flex flex-wrap lg:flex-row gap-3 items-center"> <div className="flex flex-wrap lg:flex-row gap-3 items-center">
{/* Mabes Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "mabes"} // Automatically checks if placement matches checked={data.placements === "mabes"}
disabled // To reflect read-only behavior disabled
/> />
<span>Nasional</span> <span>Nasional</span>
</label> </label>
{/* Polda Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "polda"} // Automatically checks if placement matches checked={data.placements === "polda"}
disabled disabled
/> />
<span>Wilayah</span> <span>Wilayah</span>
</label> </label>
{/* International Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "international"} // Automatically checks if placement matches checked={data.placements === "satker"}
disabled
/>
<span>Satker</span>
</label>
<label className=" cursor-pointer flex items-center gap-2">
<Checkbox
checked={data.placements === "international"}
disabled disabled
/> />
<span>International</span> <span>International</span>

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { Card } from "@/components/ui/card";
import { import {
Carousel, Carousel,
CarouselContent, CarouselContent,
@ -30,14 +31,6 @@ const TeksSliderPage = () => {
initFetch(); initFetch();
}, [page, limit, search]); }, [page, limit, search]);
// useEffect(() => {
// if (documentData?.length > 0) {
// shuffleAndSetVideos();
// const interval = setInterval(shuffleAndSetVideos, 5000);
// return () => clearInterval(interval); // Cleanup interval on unmount
// }
// }, [documentData]);
const initFetch = async () => { const initFetch = async () => {
const response = await listCuratedContent(search, limit, page - 1, 3, "1"); const response = await listCuratedContent(search, limit, page - 1, 3, "1");
console.log(response); console.log(response);
@ -47,77 +40,85 @@ const TeksSliderPage = () => {
setHasData(displayDocument && displayDocument.length > 0); setHasData(displayDocument && displayDocument.length > 0);
setDisplayDocument(contentData); setDisplayDocument(contentData);
}; };
// const shuffleAndSetVideos = () => {
// const shuffled = shuffleArray([...documentData]);
// setDisplayDocument(shuffled.slice(0, 2));
// };
// const shuffleArray = (array: any[]) => {
// for (let i = array.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [array[i], array[j]] = [array[j], array[i]];
// }
// return array;
// };
return ( return (
<Carousel className="w-full pr-3"> <div className="w-full px-2">
<CarouselContent> {displayDocument.length > 0 && (
<CarouselItem> <div>
<div className="p-1 flex flex-row md:basis-1/2 lg:basis-1/3 gap-3"> <div className="flex justify-between items-center mb-2">
{displayDocument?.map((document: any) => ( <h2 className="text-xl font-semibold">Teks</h2>
<Link <Link
href={`/shared/curated-content/giat-routine/document/detail/${document.id}`} href={"/shared/curated-content/giat-routine/video/all"}
key={document?.id} className="text-sm text-gray-500 hover:text-gray-700 flex items-center"
className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full" >
> Lihat Semua <Icon icon="lucide:arrow-right" className="ml-1" />
<div className="flex items-center justify-center rounded-lg w-16 h-16"> </Link>
<svg
width="28"
height="34"
viewBox="0 0 28 34"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
fill="black"
/>
</svg>
</div>
<div className="flex flex-col flex-1">
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
{formatDateToIndonesian(new Date(document?.createdAt))}{" "}
{document?.timezone ? document?.timezone : "WIB"} |{" "}
<Icon icon="formkit:eye" width="15" height="15" /> 518
</div>
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
{document?.title}
</div>
<div className="flex gap-2 items-center text-sm text-red-500 dark:text-red-500">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 512 512"
>
<path
fill="#f00"
d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z"
/>
</svg>
Download Dokumen
</div>
</div>
</Link>
))}
</div> </div>
</CarouselItem> <Carousel className="w-full">
</CarouselContent> <CarouselContent>
<CarouselPrevious /> {displayDocument.map((document, index) => (
<CarouselNext /> <CarouselItem key={index} className="md:basis-1/3 lg:basis-1/3">
</Carousel> <div className="p-2">
<Card className=" shadow-md rounded-lg overflow-hidden">
<Link
href={`/shared/curated-content/giat-routine/document/detail/${document.id}`}
key={document?.id}
className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
>
<div className="flex items-center justify-center rounded-lg w-16 h-16">
<svg
width="28"
height="34"
viewBox="0 0 28 34"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
fill="black"
/>
</svg>
</div>
<div className="flex flex-col flex-1">
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
{formatDateToIndonesian(
new Date(document?.createdAt)
)}{" "}
{document?.timezone ? document?.timezone : "WIB"} |{" "}
<Icon icon="formkit:eye" width="15" height="15" />{" "}
518
</div>
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
{document?.title}
</div>
<div className="flex gap-2 items-center text-sm text-red-500 dark:text-red-500">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 512 512"
>
<path
fill="#f00"
d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z"
/>
</svg>
Download Dokumen
</div>
</div>
</Link>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
)}
</div>
); );
}; };

View File

@ -52,9 +52,7 @@ const ImageAllPage = () => {
</div> </div>
</div> </div>
<div className="ml-5 pb-3"> <div className="ml-5 pb-3">
<div className="flex justify-between items-center mx-3"> <div className="flex justify-between items-center mx-3"></div>
<Label className="text-base">Image</Label>
</div>
<div className="px-5 my-5"> <div className="px-5 my-5">
<ImageSliderPage /> <ImageSliderPage />
</div> </div>

View File

@ -55,6 +55,7 @@ import { getCookiesDecrypt } from "@/lib/utils";
import { close, loading } from "@/lib/swal"; import { close, loading } from "@/lib/swal";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { htmlToString } from "@/utils/globals"; import { htmlToString } from "@/utils/globals";
import { Link } from "@/i18n/routing";
const detailSchema = z.object({ const detailSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -443,8 +444,13 @@ export default function DetailImage() {
<p className="text-blue-500">Kotak Saran (0)</p> <p className="text-blue-500">Kotak Saran (0)</p>
</div> </div>
</div> </div>
<Link
<Button>Content Rewrite</Button> href={
"/shared/curated-content/giat-routine/image/detail/content-rewrite"
}
>
<Button>Content Rewrite</Button>
</Link>
</div> </div>
</div> </div>
</div> </div>
@ -534,25 +540,31 @@ export default function DetailImage() {
{/* Mabes Checkbox */} {/* Mabes Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "mabes"} // Automatically checks if placement matches checked={data.placements === "mabes"}
disabled // To reflect read-only behavior disabled
/> />
<span>Nasional</span> <span>Nasional</span>
</label> </label>
{/* Polda Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "polda"} // Automatically checks if placement matches checked={data.placements === "polda"}
disabled disabled
/> />
<span>Wilayah</span> <span>Wilayah</span>
</label> </label>
{/* International Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "international"} // Automatically checks if placement matches checked={data.placements === "satker"}
disabled
/>
<span>Satker</span>
</label>
<label className=" cursor-pointer flex items-center gap-2">
<Checkbox
checked={data.placements === "international"}
disabled disabled
/> />
<span>International</span> <span>International</span>

View File

@ -0,0 +1,349 @@
"use client";
import { useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Controller, useForm } from "react-hook-form";
import CustomEditor from "@/components/editor/custom-editor";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Checkbox } from "@/components/ui/checkbox";
import { Link } from "@/i18n/routing";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
const ContentRewritePage = () => {
const [step, setStep] = useState("configuration");
const [selectedLanguage, setSelectedLanguage] = useState("");
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
const [selectedSize, setSelectedSize] = useState("");
const [selectedSort, setSelectedSort] = useState("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
);
type ImageSchema = z.infer<typeof imageSchema>;
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
return (
<div className="p-6">
<Card>
<CardContent className="p-6">
<h2 className="text-xl font-semibold mb-4">Content Rewrite</h2>
<div className="flex items-center space-x-6 mb-6">
<div className="flex flex-col items-center">
<div
className={`w-8 h-8 flex items-center justify-center border-2 rounded-full ${
step === "configuration"
? "border-blue-500 text-blue-500"
: "border-gray-400 text-gray-400"
}`}
>
</div>
<p
className={
step === "configuration"
? "text-blue-500 font-semibold"
: "text-gray-400"
}
>
Configuration
</p>
</div>
<div
className={`flex-1 h-0.5 ${
step === "draft"
? "bg-blue-500 text-blue-500"
: "bg-gray-400 text-gray-400"
}`}
></div>
<div className="flex flex-col items-center">
<div
className={`w-8 h-8 flex items-center justify-center border-2 rounded-full ${
step === "draft"
? "border-blue-500 text-blue-500"
: "border-gray-400 text-gray-400"
}`}
>
</div>
<p
className={
step === "draft"
? "text-blue-500 font-semibold"
: "text-gray-400"
}
>
Draft
</p>
</div>
<div
className={`flex-1 h-0.5 ${
step === "publish"
? "bg-blue-500 text-blue-500"
: "bg-gray-400 text-gray-400"
}`}
></div>
<div className="flex flex-col items-center">
<div
className={`w-8 h-8 flex items-center justify-center border-2 rounded-full ${
step === "publish"
? "border-blue-500 text-blue-500"
: "border-gray-400 text-gray-400"
}`}
>
</div>
<p
className={
step === "publish"
? "text-blue-500 font-semibold"
: "text-gray-400"
}
>
Publish
</p>
</div>
</div>
{step === "configuration" && (
<>
<div className="flex flex-row gap-3">
<div className="space-y-2 py-3 w-4/12">
<Label>Bahasa</Label>
<Select onValueChange={setSelectedLanguage}>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
<SelectItem value="id">Indonesia</SelectItem>
<SelectItem value="en">English</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2 py-3 w-4/12">
<Label>Writing Style</Label>
<Select onValueChange={setSelectedWritingStyle}>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
<SelectItem value="friendly">Friendly</SelectItem>
<SelectItem value="profesional">Profesional</SelectItem>
<SelectItem value="informational">
Informational
</SelectItem>
<SelectItem value="neutral">Neutral</SelectItem>
<SelectItem value="witty">Witty</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2 py-3 w-4/12">
<Label>Article Size</Label>
<Select onValueChange={setSelectedSize}>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
<SelectItem value="news">
News (300 - 900 words)
</SelectItem>
<SelectItem value="info">
Info (900 - 2000 words)
</SelectItem>
<SelectItem value="detail">
Detail (2000 - 5000 words)
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="mb-4">
<Label>Configuration</Label>
<Input
placeholder="Type your custom instruction here!"
className="h-20"
/>
</div>
<Button
onClick={() => setStep("draft")}
className=" bg-blue-600 text-white"
>
Selanjutnya
</Button>
</>
)}
{step === "draft" && (
<div>
<div className="flex flex-row justify-between">
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
// checked={filtered.includes("polri")}
// onCheckedChange={(e) => handleFilter("polri", Boolean(e))}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Select All
</label>
</div>
<div className="space-y-2 py-3">
<div className="flex flex-row items-center">
<Label className="w-[50px]">Sort by</Label>
<Select onValueChange={setSelectedSort}>
<SelectTrigger size="sm" className="w-[150px]">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
<SelectItem value="id">Indonesia</SelectItem>
<SelectItem value="en">English</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-4">
{[
{
src: "/assets/img/image1.png",
alt: "Article 1",
title: "Kurang dari 24 Jam Polres Muara Enim Ungka...",
},
{
src: "/assets/img/image3.png",
alt: "Article 2",
title: "Kurang dari 24 Jam Polres Muara Enim Ungka...",
},
{
src: "/assets/img/image3.png",
alt: "Article 3",
title: "Polres Magelang Kota Konferensi Pers Terkait...",
},
].map((article, index) => (
<div
key={index}
className="border rounded-md overflow-hidden relative"
>
<input
type="checkbox"
className="absolute top-2 left-2 w-5 h-5 cursor-pointer"
/>
<img
src={article.src}
alt={article.alt}
className="w-full h-40 object-cover"
/>
<p className="p-2 text-sm">{article.title}</p>
</div>
))}
</div>
<div className="flex flex-row justify-between mt-3">
<Button
onClick={() => setStep("configuration")}
variant={"outline"}
color="primary"
>
Kembali
</Button>
<Button
onClick={() => setStep("publish")}
variant={"default"}
color="primary"
>
Selanjutnya
</Button>
</div>
</div>
)}
{step === "publish" && (
<div>
<div className="py-3">
<div className="flex flex-row justify-between items-center mb-3">
<Label>940 Words</Label>
<Link
href={`/contributor/content/image/update-seo/${selectedArticleId}`}
>
<Button
className="mb-2"
size="sm"
variant={"outline"}
color="primary"
>
Edit
</Button>
</Link>
</div>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
// initialData={articleBody}
/>
)}
/>
{/* {errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)} */}
</div>
<div className="flex flex-row justify-between mt-3">
<Button
onClick={() => setStep("configuration")}
variant={"outline"}
color="primary"
>
Kembali
</Button>
<Button
onClick={() => setStep("publish")}
variant={"default"}
color="primary"
>
Selanjutnya
</Button>
</div>
</div>
)}
</CardContent>
</Card>
</div>
);
};
export default ContentRewritePage;

View File

@ -10,123 +10,95 @@ import {
} from "@/components/ui/carousel"; } from "@/components/ui/carousel";
import { listCuratedContent } from "@/service/curated-content/curated-content"; import { listCuratedContent } from "@/service/curated-content/curated-content";
import { import { formatDateToIndonesian } from "@/utils/globals";
formatDateToIndonesian,
generateLocalizedPath,
textEllipsis,
} from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import { import { useRouter } from "next/navigation";
SortingState, import React, { useEffect, useState } from "react";
ColumnFiltersState,
VisibilityState, type ImageData = {
PaginationState, id: string;
} from "@tanstack/react-table"; title: string;
import { createdAt: string;
useParams, timezone: string;
usePathname, thumbnailLink: string;
useRouter, clickCount: string;
useSearchParams, };
} from "next/navigation";
import React, { Component, useEffect, useState } from "react";
const ImageSliderPage = () => { const ImageSliderPage = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const [imageData, setImageData] = useState<ImageData[]>([]);
const [imageData, setImageData] = useState<any>();
const [displayImage, setDisplayImage] = useState<any[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [limit, setLimit] = React.useState(10); const [limit] = useState(10);
const [search, setSearch] = React.useState("");
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, [page, limit, search]); }, [page]);
// useEffect(() => {
// if (imageData?.length > 0) {
// shuffleAndSetVideos();
// const interval = setInterval(shuffleAndSetVideos, 5000);
// return () => clearInterval(interval); // Cleanup interval on unmount
// }
// }, [imageData]);
const fetchData = async () => { const fetchData = async () => {
const response = await listCuratedContent(search, limit, page - 1, 1, "1"); const response = await listCuratedContent("", limit, page - 1, 1, "1");
console.log(response); const data = response?.data?.data?.content || [];
setImageData(data);
const data = response?.data?.data;
const contentData = data?.content;
setImageData(contentData);
}; };
// const shuffleAndSetVideos = () => {
// const shuffled = shuffleArray([...imageData]);
// setDisplayImage(shuffled.slice(0, 3));
// };
// const shuffleArray = (array: any[]) => {
// for (let i = array.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [array[i], array[j]] = [array[j], array[i]];
// }
// return array;
// };
return ( return (
<Carousel className="w-full pr-3"> <div className="w-full px-2">
<CarouselContent> {imageData.length > 0 && (
{Array.from({ length: 5 }).map((_, index) => ( <div>
<CarouselItem key={index}> <div className="flex justify-between items-center mb-2">
<div className="p-1 flex flex-row md:basis-1/2 lg:basis-1/2 gap-3"> <h2 className="text-xl font-semibold">Foto</h2>
{imageData?.map((image: any) => ( <Link
<Card href={"/shared/curated-content/giat-routine/image/all"}
key={image?.id} className="text-sm text-gray-500 hover:text-gray-700 flex items-center"
className="hover:sc ale-110 transition-transform duration-300" >
> Lihat Semua <Icon icon="lucide:arrow-right" className="ml-1" />
<Link </Link>
href={`/shared/curated-content//giat-routine/image/detail/${image.id}`} </div>
> <Carousel className="w-full">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0"> <CarouselContent>
<img {imageData.map((image, index) => (
src={image?.thumbnailLink} <CarouselItem key={index} className="md:basis-1/3 lg:basis-1/3">
className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" <div className="p-2">
/> <Card className="shadow-md rounded-lg overflow-hidden">
<div className="flex flex-row items-center gap-2 text-[10px] mx-2"> <Link
{formatDateToIndonesian(new Date(image?.createdAt))}{" "} href={`/shared/curated-content/giat-routine/image/detail/${image.id}`}
{image?.timezone ? image?.timezone : "WIB"}|{" "} >
<Icon icon="formkit:eye" width="15" height="15" /> <CardContent className="p-0">
{image?.clickCount}{" "} <img
<svg src={image?.thumbnailLink}
xmlns="http://www.w3.org/2000/svg" alt={image?.title}
width="1em" className="w-full h-56 object-cover rounded-t-lg"
height="1em"
viewBox="0 0 20 20"
>
<path
fill="#f00"
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
/> />
</svg>{" "} <div className="p-3">
</div> <p className="text-xs text-gray-500">
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full"> {formatDateToIndonesian(
{image?.title} new Date(image?.createdAt)
</div> )}{" "}
</CardContent> {image?.timezone || "WIB"} |
</Link> <Icon
</Card> icon="formkit:eye"
width="15"
height="15"
className="inline ml-1"
/>
{image?.clickCount}
</p>
<h3 className="font-semibold text-sm truncate">
{image?.title}
</h3>
</div>
</CardContent>
</Link>
</Card>
</div>
</CarouselItem>
))} ))}
</div> </CarouselContent>
</CarouselItem> <CarouselPrevious />
))} <CarouselNext />
</CarouselContent> </Carousel>
<CarouselPrevious /> </div>
<CarouselNext /> )}
</Carousel> </div>
// <div className="mx-3 px-5">
// <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
// </div>
// </div>
); );
}; };

View File

@ -8,6 +8,7 @@ import {
CarouselNext, CarouselNext,
CarouselPrevious, CarouselPrevious,
} from "@/components/ui/carousel"; } from "@/components/ui/carousel";
import { Label } from "@/components/ui/label";
import { listCuratedContent } from "@/service/curated-content/curated-content"; import { listCuratedContent } from "@/service/curated-content/curated-content";
import { getListContent } from "@/service/landing/landing"; import { getListContent } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals"; import { formatDateToIndonesian } from "@/utils/globals";
@ -15,9 +16,18 @@ import { Icon } from "@iconify/react/dist/iconify.js";
import image from "next/image"; import image from "next/image";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
type VideoData = {
id: string;
title: string;
createdAt: string;
timezone: string;
thumbnailLink: string;
clickCount: string;
};
const VideoSliderPage = () => { const VideoSliderPage = () => {
const [allVideoData, setAllVideoData] = useState<any[]>([]); const [allVideoData, setAllVideoData] = useState<any[]>([]);
const [displayVideos, setDisplayVideos] = useState<any[]>([]); const [videoData, setVideoData] = useState<VideoData[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [limit, setLimit] = React.useState(10); const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
@ -26,86 +36,74 @@ const VideoSliderPage = () => {
fetchData(); fetchData();
}, [page, limit, search]); }, [page, limit, search]);
// useEffect(() => {
// if (allVideoData?.length > 0) {
// shuffleAndSetVideos();
// const interval = setInterval(shuffleAndSetVideos, 5000);
// return () => clearInterval(interval); // Cleanup interval on unmount
// }
// }, [allVideoData]);
const fetchData = async () => { const fetchData = async () => {
const response = await listCuratedContent(search, limit, page - 1, 2, "1"); const response = await listCuratedContent(search, limit, page - 1, 2, "1");
console.log(response); console.log(response);
const data = response?.data?.data; const data = response?.data?.data;
const contentData = data?.content; const contentData = data?.content;
setAllVideoData(contentData); setVideoData(contentData);
}; };
// const shuffleAndSetVideos = () => {
// const shuffled = shuffleArray([...allVideoData]);
// setDisplayVideos(shuffled.slice(0, 3));
// };
// const shuffleArray = (array: any[]) => {
// for (let i = array.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [array[i], array[j]] = [array[j], array[i]];
// }
// return array;
// };
return ( return (
<Carousel className="w-full pr-3"> <div className="w-full px-2">
<CarouselContent> {videoData.length > 0 && (
{Array.from({ length: 5 }).map((_, index) => ( <div>
<CarouselItem key={index}> <div className="flex justify-between items-center mb-2">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> <h2 className="text-xl font-semibold">Video</h2>
{allVideoData.map((video: any) => ( <Link
<Card href={"/shared/curated-content/giat-routine/video/all"}
key={video?.id} className="text-sm text-gray-500 hover:text-gray-700 flex items-center"
className="hover:scale-110 transition-transform duration-300" >
> Lihat Semua <Icon icon="lucide:arrow-right" className="ml-1" />
<Link </Link>
href={`/shared/curated-content/giat-routine/video/detail/${video.id}`} </div>
> <Carousel className="w-full">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0"> <CarouselContent>
<img {videoData.map((video, index) => (
src={video?.thumbnailLink} <CarouselItem key={index} className="md:basis-1/3 lg:basis-1/3">
className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" <div className="p-2">
/> <Card className="shadow-md rounded-lg overflow-hidden">
<div className="flex flex-row items-center gap-2 text-[10px] mx-2"> <Link
{formatDateToIndonesian(new Date(video?.createdAt))}{" "} href={`/shared/curated-content/giat-routine/video/detail/${video.id}`}
{video?.timezone || "WIB"} | >
<Icon icon="formkit:eye" width="15" height="15" /> <CardContent className="p-0">
{video?.clickCount} <img
<svg src={video?.thumbnailLink}
xmlns="http://www.w3.org/2000/svg" alt={video?.title}
width="1em" className="w-full h-56 object-cover rounded-t-lg"
height="1em"
viewBox="0 0 20 20"
>
<path
fill="#f00"
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
/> />
</svg> <div className="p-3">
</div> <p className="text-xs text-gray-500">
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full"> {formatDateToIndonesian(
{video?.title} new Date(video?.createdAt)
</div> )}{" "}
</CardContent> {video?.timezone || "WIB"} |
</Link> <Icon
</Card> icon="formkit:eye"
width="15"
height="15"
className="inline ml-1"
/>
{video?.clickCount}
</p>
<h3 className="font-semibold text-sm truncate">
{video?.title}
</h3>
</div>
</CardContent>
</Link>
</Card>
</div>
</CarouselItem>
))} ))}
</div> </CarouselContent>
</CarouselItem> <CarouselPrevious />
))} <CarouselNext />
</CarouselContent> </Carousel>
<CarouselPrevious /> </div>
<CarouselNext /> )}
</Carousel> </div>
); );
}; };

View File

@ -523,39 +523,39 @@ export default function DetailImage() {
key={data.id} key={data.id}
className="flex items-center gap-3 mt-2" className="flex items-center gap-3 mt-2"
> >
{/* <img
className="object-cover w-20 h-20"
src={data.thumbnailUrl} // Assuming `thumbnailUrl` is the property that contains the URL for the thumbnail image
alt={`Thumbnail ${index}`}
/> */}
<img <img
className="object-cover w-20 h-20 lg:w-32 lg:h-32" className="object-cover w-20 h-20 lg:w-32 lg:h-32"
src={"/assets/video-icon.webp"} src={"/assets/video-icon.webp"}
alt={` ${data.id}`} alt={` ${data.id}`}
/> />
<div className="flex flex-wrap lg:flex-row gap-3 items-center"> <div className="flex flex-wrap lg:flex-row gap-3 items-center">
{/* Mabes Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "mabes"} // Automatically checks if placement matches checked={data.placements === "mabes"}
disabled // To reflect read-only behavior disabled
/> />
<span>Nasional</span> <span>Nasional</span>
</label> </label>
{/* Polda Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "polda"} // Automatically checks if placement matches checked={data.placements === "polda"}
disabled disabled
/> />
<span>Wilayah</span> <span>Wilayah</span>
</label> </label>
{/* International Checkbox */}
<label className=" cursor-pointer flex items-center gap-2"> <label className=" cursor-pointer flex items-center gap-2">
<Checkbox <Checkbox
checked={data.placements === "international"} // Automatically checks if placement matches checked={data.placements === "satker"}
disabled
/>
<span>Satker</span>
</label>
<label className=" cursor-pointer flex items-center gap-2">
<Checkbox
checked={data.placements === "international"}
disabled disabled
/> />
<span>International</span> <span>International</span>

View File

@ -24,15 +24,19 @@ import AudioSliderPage from "./giat-routine/audio/audio";
import ImageSliderPage from "./giat-routine/image/image"; import ImageSliderPage from "./giat-routine/image/image";
import TeksSliderPage from "./giat-routine/document/teks"; import TeksSliderPage from "./giat-routine/document/teks";
import ContestTable from "../contest/components/contest-table"; import ContestTable from "../contest/components/contest-table";
import { useTranslations } from "next-intl";
const CuratedContentPage = () => { const CuratedContentPage = () => {
const t = useTranslations("Curation");
return ( return (
<div> <div>
<SiteBreadcrumb /> <SiteBreadcrumb />
<div className="my-3"> <div className="my-3">
<Tabs defaultValue="giat-routine" className="w-full"> <Tabs defaultValue="giat-routine" className="w-full">
<Card className="py-3 px-2 my-4"> <Card className="py-3 px-2 my-4">
<p className="text-lg font-semibold ml-2">Kurasi Konten</p> <p className="text-lg font-semibold ml-2">
{t("content-curation")}
</p>
<TabsList className="flex-wrap"> <TabsList className="flex-wrap">
<TabsTrigger <TabsTrigger
value="giat-routine" value="giat-routine"
@ -73,80 +77,15 @@ const CuratedContentPage = () => {
</div> </div>
</div> </div>
<div className="ml-5 pb-3"> <div className="ml-5 pb-3">
<div className="flex justify-between items-center mx-3">
<Label className="text-base">Audio Visual</Label>
<div className="flex items-center">
<Label>
{" "}
<Link
href={
"/shared/curated-content/giat-routine/video/all"
}
>
Lihat Semua
</Link>
</Label>
<ArrowRight size={18} className="text-slate-600" />
</div>
</div>
<div className="px-5 my-5"> <div className="px-5 my-5">
<VideoSliderPage /> <VideoSliderPage />
</div> </div>
<div className="flex justify-between items-center mx-3">
<Label className="text-base">Audio</Label>
<div className="flex items-center">
<Label>
{" "}
<Link
href={
"/shared/curated-content/giat-routine/audio/all"
}
>
Lihat Semua
</Link>
</Label>
<ArrowRight size={18} className="text-slate-600" />
</div>
</div>
<div className="px-5 my-5"> <div className="px-5 my-5">
<AudioSliderPage /> <AudioSliderPage />
</div> </div>
<div className="flex justify-between items-center mx-3">
<Label className="text-base">Foto</Label>
<div className="flex items-center">
<Label>
{" "}
<Link
href={
"/shared/curated-content/giat-routine/image/all"
}
>
Lihat Semua
</Link>
</Label>
<ArrowRight size={18} className="text-slate-600" />
</div>
</div>
<div className="px-5 my-5"> <div className="px-5 my-5">
<ImageSliderPage /> <ImageSliderPage />
</div> </div>
<div className="flex justify-between items-center mx-3">
<Label className="text-base">Teks</Label>
<div className="flex items-center">
<Label>
{" "}
<Link
href={
"/shared/curated-content/giat-routine/document/all"
}
>
Lihat Semua
</Link>
</Label>
<ArrowRight size={18} className="text-slate-600" />
</div>
</div>
<div className="px-5 my-5"> <div className="px-5 my-5">
<TeksSliderPage /> <TeksSliderPage />
</div> </div>

View File

@ -442,7 +442,22 @@ export default function FormAudio() {
return; return;
} }
const requestData = { let requestData: {
title: string;
description: string;
htmlDescription: string;
fileTypeId: string;
categoryId: any;
subCategoryId: any;
uploadedBy: string;
statusId: string;
publishedFor: string;
creatorName: string;
tags: string;
isYoutube: boolean;
isInternationalMedia: boolean;
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
} = {
...data, ...data,
title: finalTitle, title: finalTitle,
description: finalDescription, description: finalDescription,
@ -461,6 +476,10 @@ export default function FormAudio() {
let id = Cookies.get("idCreate"); let id = Cookies.get("idCreate");
if (scheduleId !== undefined) {
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini
}
if (id == undefined) { if (id == undefined) {
const response = await createMedia(requestData); const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData); console.log("Form Data Submitted:", requestData);

View File

@ -56,6 +56,7 @@ import { options } from "@fullcalendar/core/preact.js";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth"; import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import { request } from "http";
interface FileWithPreview extends File { interface FileWithPreview extends File {
preview: string; preview: string;
@ -443,11 +444,28 @@ export default function FormImage() {
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description; const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) { if (!finalDescription.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
const requestData = {
let requestData: {
title: string;
description: string;
htmlDescription: string;
fileTypeId: string;
categoryId: any;
subCategoryId: any;
uploadedBy: string;
statusId: string;
publishedFor: string;
creatorName: string;
tags: string;
isYoutube: boolean;
isInternationalMedia: boolean;
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
} = {
...data, ...data,
title: finalTitle, title: finalTitle,
description: finalDescription, description: finalDescription,
@ -466,14 +484,14 @@ export default function FormImage() {
let id = Cookies.get("idCreate"); let id = Cookies.get("idCreate");
if (scheduleId !== undefined) {
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini
}
if (id == undefined) { if (id == undefined) {
const response = await createMedia(requestData); const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData); console.log("Form Data Submitted:", requestData);
// if (response?.error) {
// MySwal.fire("Error", response?.message, "error");
// return;
// }
Cookies.set("idCreate", response?.data?.data, { expires: 1 }); Cookies.set("idCreate", response?.data?.data, { expires: 1 });
id = response?.data?.data; id = response?.data?.data;
@ -489,16 +507,15 @@ export default function FormImage() {
} }
// Upload File // Upload File
const progressInfoArr = []; const progressInfoArr = files.map((item) => ({
for (const item of files) { percentage: 0,
progressInfoArr.push({ percentage: 0, fileName: item.name }); fileName: item.name,
} }));
progressInfo = progressInfoArr; progressInfo = progressInfoArr;
setIsStartUpload(true); setIsStartUpload(true);
setProgressList(progressInfoArr); setProgressList(progressInfoArr);
close(); close();
// showProgress();
files.map(async (item: any, index: number) => { files.map(async (item: any, index: number) => {
await uploadResumableFile( await uploadResumableFile(
index, index,
@ -509,8 +526,6 @@ export default function FormImage() {
}); });
Cookies.remove("idCreate"); Cookies.remove("idCreate");
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
}; };
const onSubmit = (data: ImageSchema) => { const onSubmit = (data: ImageSchema) => {

View File

@ -394,6 +394,33 @@ export default function FormConvertSPIT() {
return temp; return temp;
}; };
// const setupPlacement = (
// index: number,
// category: string,
// isChecked: boolean
// ) => {
// setFilePlacements((prev) =>
// prev.map((placement, i) =>
// i === index
// ? isChecked
// ? [...new Set([...placement, category])] // Tambahkan kategori jika belum ada
// : placement.filter((item) => item !== category) // Hapus kategori jika ada
// : placement
// )
// );
// };
const handleSelectAll = (category: string, isChecked: boolean) => {
setFilePlacements((prev: string[][]) =>
prev.map(
(placement: string[]) =>
isChecked
? Array.from(new Set([...placement, category])) // Konversi Set ke array dengan Array.from()
: placement.filter((item: string) => item !== category) // Hapus jika ada
)
);
};
const save = async (data: { const save = async (data: {
contentTitle: string; contentTitle: string;
contentDescription: string; contentDescription: string;
@ -785,6 +812,66 @@ export default function FormConvertSPIT() {
<div className="mt-3"> <div className="mt-3">
<Label className="text-xl">Penempatan file</Label> <Label className="text-xl">Penempatan file</Label>
</div> </div>
{files?.length > 1 && (
<div className="flex flex-wrap gap-2 mt-2">
<div className="flex items-center space-x-2">
<Checkbox
id="all-content"
onCheckedChange={(e) =>
handleSelectAll("all", Boolean(e))
}
/>
<label
htmlFor="all-content"
className="text-xs font-medium"
>
All
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all-nasional"
onCheckedChange={(e) =>
handleSelectAll("mabes", Boolean(e))
}
/>
<label
htmlFor="all-nasional"
className="text-xs font-medium"
>
All Nasional
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all-wilayah"
onCheckedChange={(e) =>
handleSelectAll("polda", Boolean(e))
}
/>
<label
htmlFor="all-wilayah"
className="text-xs font-medium"
>
All Wilayah
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all-international"
onCheckedChange={(e) =>
handleSelectAll("international", Boolean(e))
}
/>
<label
htmlFor="all-international"
className="text-xs font-medium"
>
All Internasional
</label>
</div>
</div>
)}
{files?.map((file, index) => ( {files?.map((file, index) => (
<div <div
key={file.contentId} key={file.contentId}

View File

@ -441,7 +441,22 @@ export default function FormTeks() {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
const requestData = { let requestData: {
title: string;
description: string;
htmlDescription: string;
fileTypeId: string;
categoryId: any;
subCategoryId: any;
uploadedBy: string;
statusId: string;
publishedFor: string;
creatorName: string;
tags: string;
isYoutube: boolean;
isInternationalMedia: boolean;
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
} = {
...data, ...data,
title: finalTitle, title: finalTitle,
description: finalDescription, description: finalDescription,
@ -460,6 +475,10 @@ export default function FormTeks() {
let id = Cookies.get("idCreate"); let id = Cookies.get("idCreate");
if (scheduleId !== undefined) {
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini
}
if (id == undefined) { if (id == undefined) {
const response = await createMedia(requestData); const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData); console.log("Form Data Submitted:", requestData);

View File

@ -441,7 +441,22 @@ export default function FormVideo() {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
const requestData = { let requestData: {
title: string;
description: string;
htmlDescription: string;
fileTypeId: string;
categoryId: any;
subCategoryId: any;
uploadedBy: string;
statusId: string;
publishedFor: string;
creatorName: string;
tags: string;
isYoutube: boolean;
isInternationalMedia: boolean;
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
} = {
...data, ...data,
title: finalTitle, title: finalTitle,
description: finalDescription, description: finalDescription,
@ -460,6 +475,10 @@ export default function FormVideo() {
let id = Cookies.get("idCreate"); let id = Cookies.get("idCreate");
if (scheduleId !== undefined) {
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini
}
if (id == undefined) { if (id == undefined) {
const response = await createMedia(requestData); const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData); console.log("Form Data Submitted:", requestData);

View File

@ -150,6 +150,7 @@ export default function FormContestDetail() {
mabes: false, mabes: false,
polda: false, polda: false,
polres: false, polres: false,
satker: false,
}); });
const { const {
@ -224,18 +225,17 @@ export default function FormContestDetail() {
}, [detail?.targetOutput]); }, [detail?.targetOutput]);
useEffect(() => { useEffect(() => {
if (detail?.targetParticipantTopLevel) { if (detail?.targetOutput) {
const outputSet = new Set( const outputSet = new Set(detail.targetOutput.split(",").map(Number));
detail.targetParticipantTopLevel.split(",").map(Number)
);
setUnitSelection({ setUnitSelection({
allUnit: outputSet.has(0), allUnit: outputSet.has(0),
mabes: outputSet.has(1), mabes: outputSet.has(1),
polda: outputSet.has(2), polda: outputSet.has(2),
polres: outputSet.has(3), polres: outputSet.has(3),
satker: outputSet.has(4),
}); });
} }
}, [detail?.targetParticipantTopLevel]); }, [detail?.targetOutput]);
const handleCheckboxChange = (levelId: number) => { const handleCheckboxChange = (levelId: number) => {
setCheckedLevels((prev) => { setCheckedLevels((prev) => {
@ -267,6 +267,7 @@ export default function FormContestDetail() {
mabes: "1", mabes: "1",
polda: "2", polda: "2",
polres: "3", polres: "3",
satker: "4",
}; };
const assignmentPurposeString = Object.keys(unitSelection) const assignmentPurposeString = Object.keys(unitSelection)
@ -436,7 +437,7 @@ export default function FormContestDetail() {
fileTypeId: string, fileTypeId: string,
duration: string duration: string
) { ) {
console.log(idx, id, file, fileTypeId, duration); console.log("Param Upload : ", idx, id, file, fileTypeId, duration);
const resCsrf = await getCsrfToken(); const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token; const csrfToken = resCsrf?.data?.token;
@ -451,7 +452,7 @@ export default function FormContestDetail() {
retryDelays: [0, 3000, 6000, 12_000, 24_000], retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000, chunkSize: 20_000,
metadata: { metadata: {
assignmentId: id, contestId: id,
filename: file.name, filename: file.name,
contentType: file.type, contentType: file.type,
fileTypeId: fileTypeId, fileTypeId: fileTypeId,

View File

@ -17,7 +17,7 @@ import {
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CalendarIcon, Clock1, MapPin, Plus, User2 } from "lucide-react"; import { CalendarIcon, Clock1, Eye, MapPin, Plus, User2 } from "lucide-react";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { addDays, format, parseISO, setDate } from "date-fns"; import { addDays, format, parseISO, setDate } from "date-fns";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
@ -30,6 +30,7 @@ import { error, loading } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { import {
detailSchedule, detailSchedule,
getListScheduleAttachment,
listScheduleNext, listScheduleNext,
listScheduleToday, listScheduleToday,
postSchedule, postSchedule,
@ -61,6 +62,14 @@ interface Detail {
addressLong: number; addressLong: number;
} }
interface Attachment {
id: any;
title: string;
fileTypeId: number;
type: number;
fileTypeName: string;
}
export default function FormEventDetail() { export default function FormEventDetail() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { id } = useParams() as { id: string }; const { id } = useParams() as { id: string };
@ -89,6 +98,14 @@ export default function FormEventDetail() {
}, },
}); });
const [lampiran, setDataLampiran] = useState<Attachment[]>([]);
async function getDataAttachment() {
const response = await getListScheduleAttachment(id);
console.log("data attach", response?.data?.data?.content);
setDataLampiran(response?.data?.data?.content);
}
async function getDataByDate() { async function getDataByDate() {
const resToday = await listScheduleToday(); const resToday = await listScheduleToday();
const today = resToday?.data?.data; const today = resToday?.data?.data;
@ -120,6 +137,7 @@ export default function FormEventDetail() {
} }
} }
initState(); initState();
getDataAttachment();
}, [refresh, setValue]); }, [refresh, setValue]);
const handleStartTime = (e: React.ChangeEvent<HTMLInputElement>) => { const handleStartTime = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -130,6 +148,29 @@ export default function FormEventDetail() {
setEndTime(e.target.value); setEndTime(e.target.value);
}; };
const handleDestinationUpload = (
type: number,
id: string | number,
setOpen: (open: boolean) => void,
router: any
) => {
setOpen(false); // Tutup modal dialog
if (id !== undefined) {
Cookies.set("scheduleId", id.toString(), { expires: 1 });
Cookies.set("scheduleType", "3", { expires: 1 });
const routes: Record<number, string> = {
1: "/in/contributor/schedule/media/image/create",
2: "/in/contributor/schedule/media/video/create",
3: "/in/contributor/schedule/media/text/create",
4: "/in/contributor/schedule/media/audio/create",
};
router.push(routes[type] || "/admin/schedule/media/audio/create");
}
};
return ( return (
<div className="flex flex-col lg:flex-row gap-2"> <div className="flex flex-col lg:flex-row gap-2">
<Card className="w-full lg:w-9/12"> <Card className="w-full lg:w-9/12">
@ -336,92 +377,67 @@ export default function FormEventDetail() {
<Button color="primary" size="sm" type="button"> <Button color="primary" size="sm" type="button">
<Plus /> Tambah Lampiran <Plus /> Tambah Lampiran
</Button> </Button>
<p>0 Lampiran</p> <p>{lampiran?.length || 0} Lampiran</p>
</div> </div>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-w-md p-6 bg-white rounded-lg shadow-lg h-[420px] w-[700px]"> <DialogContent className="max-w-md p-6 bg-white rounded-lg shadow-lg h-[420px] w-[700px]">
<h2 className="text-lg font-semibold"> <h2 className="text-lg font-semibold">
Pilih Jenis Lampiran Pilih Jenis Lampiran
</h2> </h2>
<div className=" space-y-4 gap-y-4"> <div className="space-y-4 gap-y-4">
<Link href={"/contributor/schedule/media/video/create"}> {[
<div className="flex flex-row items-center space-x-4"> {
<div className="flex flex-col w-4/12 items-center"> type: 2,
<img img: "/assets/img/upload-video.png",
src={"/assets/img/upload-video.png"} label: "Audio Visual",
alt={"item.label"} },
className="w-12 h-12" {
/> type: 1,
<h3 className="text-base font-semibold"> img: "/assets/img/upload-image.png",
Audio Visual label: "Foto",
</h3> },
{
type: 3,
img: "/assets/img/upload-text.png",
label: "Teks",
},
{
type: 4,
img: "/assets/img/upload-audio.png",
label: "Audio",
},
].map((item) => (
<a
key={item.type}
onClick={() =>
handleDestinationUpload(
item.type,
id,
setOpen,
router
)
}
>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={item.img}
alt={item.label}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">
{item.label}
</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa {item.label.toLowerCase()}{" "}
dengan format sesuai yang didukung.
</p>
</div>
</div> </div>
<div className="w-8/12"> </a>
<p className="text-sm text-gray-600"> ))}
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
<Link href={"/contributor/schedule/media/image/create"}>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={"/assets/img/upload-image.png"}
alt={"item.label"}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">Foto</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
<Link href={"/contributor/schedule/media/text/create"}>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={"/assets/img/upload-text.png"}
alt={"item.label"}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">Teks</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
<Link href={"/contributor/schedule/media/audio/create"}>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={"/assets/img/upload-audio.png"}
alt={"item.label"}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">Audio</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@ -430,7 +446,52 @@ export default function FormEventDetail() {
"" ""
)} )}
</div> </div>
<div className="mt-4 flex justify-end"> <div className="mt-6 mx-6">
<table className="w-full border border-gray-300 rounded-lg">
<thead className="bg-gray-200 text-left">
<tr>
<th className="p-3 font-semibold">Judul Konten</th>
<th className="p-3 font-semibold">Konten</th>
<th className="p-3 font-semibold">Tindakan</th>
<th className="p-3 font-semibold">Aksi</th>
</tr>
</thead>
<tbody>
{lampiran.map((item, index) => (
<tr key={index} className="border-t border-gray-300">
<td className="p-3 text-blue-600 cursor-pointer">
{item.title}
</td>
<td className="p-3">{item.fileTypeName}</td>
<td className="p-3 text-blue-600">Konfersi Ai</td>
<td className="p-3">
<Link
href={
Number(item.fileTypeId) == 1
? `/contributor/content/image/detail/${item.id}`
: Number(item.fileTypeId) == 2
? `/contributor/content/video/detail/${item.id}`
: Number(item.fileTypeId) == 3
? `/contributor/content/teks/detail/${item.id}`
: `/contributor/content/audio/detail/${item.id}`
}
target="_blank"
className="btn"
rel="noreferrer"
>
<Eye
className={
item.type === 1 ? "text-blue-600" : "text-black"
}
/>
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="my-4 mr-6 flex justify-end">
<Button type="submit" color="primary"> <Button type="submit" color="primary">
Submit Submit
</Button> </Button>

View File

@ -17,7 +17,7 @@ import {
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CalendarIcon, Clock1, MapPin, Plus, User2 } from "lucide-react"; import { CalendarIcon, Clock1, Eye, MapPin, Plus, User2 } from "lucide-react";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { addDays, format, parseISO, setDate } from "date-fns"; import { addDays, format, parseISO, setDate } from "date-fns";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
@ -30,6 +30,7 @@ import { error, loading } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { import {
detailSchedule, detailSchedule,
getListScheduleAttachment,
listScheduleNext, listScheduleNext,
listScheduleToday, listScheduleToday,
postSchedule, postSchedule,
@ -50,6 +51,7 @@ import {
import { formatDate } from "@fullcalendar/core/index.js"; import { formatDate } from "@fullcalendar/core/index.js";
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import $ from "jquery";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -68,6 +70,14 @@ interface Detail {
addressLong: number; addressLong: number;
} }
interface Attachment {
id: any;
title: string;
fileTypeId: number;
type: number;
fileTypeName: string;
}
export default function FormDetailPressRillis() { export default function FormDetailPressRillis() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { id } = useParams() as { id: string }; const { id } = useParams() as { id: string };
@ -98,6 +108,14 @@ export default function FormDetailPressRillis() {
}, },
}); });
const [lampiran, setDataLampiran] = useState<Attachment[]>([]);
async function getDataAttachment() {
const response = await getListScheduleAttachment(id);
console.log("data attach", response?.data?.data?.content);
setDataLampiran(response?.data?.data?.content);
}
async function getDataByDate() { async function getDataByDate() {
const resToday = await listScheduleToday(); const resToday = await listScheduleToday();
const today = resToday?.data?.data; const today = resToday?.data?.data;
@ -129,6 +147,7 @@ export default function FormDetailPressRillis() {
} }
} }
initState(); initState();
getDataAttachment();
}, [refresh, setValue]); }, [refresh, setValue]);
const handleStartTime = (e: React.ChangeEvent<HTMLInputElement>) => { const handleStartTime = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -139,20 +158,26 @@ export default function FormDetailPressRillis() {
setEndTime(e.target.value); setEndTime(e.target.value);
}; };
const handleUploadAttachment = () => { const handleDestinationUpload = (
const scheduleId = Cookies.get("scheduleId"); type: number,
id: string | number,
setOpen: (open: boolean) => void,
router: any
) => {
setOpen(false); // Tutup modal dialog
if (scheduleId == undefined) { if (id !== undefined) {
MySwal.fire({ Cookies.set("scheduleId", id.toString(), { expires: 1 });
title: "Simpan Jadwal Terlebih Dahulu", Cookies.set("scheduleType", "3", { expires: 1 });
icon: "info",
confirmButtonColor: "#3085d6", const routes: Record<number, string> = {
confirmButtonText: "Ok", 1: "/in/contributor/schedule/media/image/create",
}).then((result) => { 2: "/in/contributor/schedule/media/video/create",
if (result.isConfirmed) { 3: "/in/contributor/schedule/media/text/create",
return true; 4: "/in/contributor/schedule/media/audio/create",
} };
});
router.push(routes[type] || "/admin/schedule/media/audio/create");
} }
}; };
@ -379,92 +404,67 @@ export default function FormDetailPressRillis() {
<Button color="primary" size="sm" type="button"> <Button color="primary" size="sm" type="button">
<Plus /> Tambah Lampiran <Plus /> Tambah Lampiran
</Button> </Button>
<p>0 Lampiran</p> <p>{lampiran?.length || 0} Lampiran</p>
</div> </div>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-w-md p-6 bg-white rounded-lg shadow-lg h-[420px] w-[700px]"> <DialogContent className="max-w-md p-6 bg-white rounded-lg shadow-lg h-[420px] w-[700px]">
<h2 className="text-lg font-semibold"> <h2 className="text-lg font-semibold">
Pilih Jenis Lampiran Pilih Jenis Lampiran
</h2> </h2>
<div className=" space-y-4 gap-y-4"> <div className="space-y-4 gap-y-4">
<Link href={"/contributor/schedule/media/video/create"}> {[
<div className="flex flex-row items-center space-x-4"> {
<div className="flex flex-col w-4/12 items-center"> type: 2,
<img img: "/assets/img/upload-video.png",
src={"/assets/img/upload-video.png"} label: "Audio Visual",
alt={"item.label"} },
className="w-12 h-12" {
/> type: 1,
<h3 className="text-base font-semibold"> img: "/assets/img/upload-image.png",
Audio Visual label: "Foto",
</h3> },
{
type: 3,
img: "/assets/img/upload-text.png",
label: "Teks",
},
{
type: 4,
img: "/assets/img/upload-audio.png",
label: "Audio",
},
].map((item) => (
<a
key={item.type}
onClick={() =>
handleDestinationUpload(
item.type,
id,
setOpen,
router
)
}
>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={item.img}
alt={item.label}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">
{item.label}
</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa {item.label.toLowerCase()}{" "}
dengan format sesuai yang didukung.
</p>
</div>
</div> </div>
<div className="w-8/12"> </a>
<p className="text-sm text-gray-600"> ))}
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
<Link href={"/contributor/schedule/media/image/create"}>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={"/assets/img/upload-image.png"}
alt={"item.label"}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">Foto</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
<Link href={"/contributor/schedule/media/text/create"}>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={"/assets/img/upload-text.png"}
alt={"item.label"}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">Teks</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
<Link href={"/contributor/schedule/media/audio/create"}>
<div className="flex flex-row items-center space-x-4">
<div className="flex flex-col w-4/12 items-center">
<img
src={"/assets/img/upload-audio.png"}
alt={"item.label"}
className="w-12 h-12"
/>
<h3 className="text-base font-semibold">Audio</h3>
</div>
<div className="w-8/12">
<p className="text-sm text-gray-600">
Unggah media berupa video dengan format avi, wmv,
atau mp4 dengan ukuran minimal 2mb dan maksimal
500mb.
</p>
</div>
</div>
</Link>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@ -472,6 +472,51 @@ export default function FormDetailPressRillis() {
) : ( ) : (
"" ""
)} )}
<div className="mt-6">
<table className="w-full border border-gray-300 rounded-lg">
<thead className="bg-gray-200 text-left">
<tr>
<th className="p-3 font-semibold">Judul Konten</th>
<th className="p-3 font-semibold">Konten</th>
<th className="p-3 font-semibold">Tindakan</th>
<th className="p-3 font-semibold">Aksi</th>
</tr>
</thead>
<tbody>
{lampiran.map((item, index) => (
<tr key={index} className="border-t border-gray-300">
<td className="p-3 text-blue-600 cursor-pointer">
{item.title}
</td>
<td className="p-3">{item.fileTypeName}</td>
<td className="p-3 text-blue-600">Konfersi Ai</td>
<td className="p-3">
<Link
href={
Number(item.fileTypeId) == 1
? `/contributor/content/image/detail/${item.id}`
: Number(item.fileTypeId) == 2
? `/contributor/content/video/detail/${item.id}`
: Number(item.fileTypeId) == 3
? `/contributor/content/teks/detail/${item.id}`
: `/contributor/content/audio/detail/${item.id}`
}
target="_blank"
className="btn"
rel="noreferrer"
>
<Eye
className={
item.type === 1 ? "text-blue-600" : "text-black"
}
/>
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="mt-4 flex justify-end"> <div className="mt-4 flex justify-end">
<Button type="submit" color="primary"> <Button type="submit" color="primary">
Submit Submit

View File

@ -83,7 +83,7 @@ const Division = () => {
<div className="mx-auto px-4 w-full"> <div className="mx-auto px-4 w-full">
{/* Header */} {/* Header */}
{/* <Reveal> */} {/* <Reveal> */}
{/* <h2 className="text-center text-2xl font-bold text-gray-800 dark:text-white mb-4"> {/* <h2 className="text-center text-2xl font-bold text-gray-800 dark:text-white mb-3">
{pathname?.split("/")[1] == "in" ? ( {pathname?.split("/")[1] == "in" ? (
<> <>
{t("coverageOnly")}&nbsp;<span className="text-[#bb3523]">{t("division")}</span>{" "} {t("coverageOnly")}&nbsp;<span className="text-[#bb3523]">{t("division")}</span>{" "}

View File

@ -3,6 +3,17 @@ import React, { useEffect } from "react";
import DateRangePicker from "@/components/date-range-picker"; import DateRangePicker from "@/components/date-range-picker";
import { usePathname } from "@/components/navigation"; import { usePathname } from "@/components/navigation";
import { cn, getCookiesDecrypt } from "@/lib/utils"; import { cn, getCookiesDecrypt } from "@/lib/utils";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "./ui/dialog";
import { Button } from "./ui/button";
import { Label } from "./ui/label";
import { Input } from "./ui/input";
const PageTitle = ({ const PageTitle = ({
title, title,
@ -30,7 +41,35 @@ const PageTitle = ({
<div className="text-2xl font-medium text-default-800 capitalize"> <div className="text-2xl font-medium text-default-800 capitalize">
Dashboard Dashboard
</div> </div>
<DateRangePicker /> <Dialog>
<DialogTrigger asChild>
<Button variant="outline">Download Report</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Download Report</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="w-full">
<Label>Date</Label>
<Input
type="date"
// value={dateFilter}
// onChange={(e) => setDateFilter(e.target.value)}
className="w-full"
/>
</div>
</div>
<DialogFooter>
<Button
type="submit"
// onClick={downloadReport}
>
Download
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div> </div>
); );
}; };

View File

@ -160,7 +160,8 @@ const LoginForm = () => {
Number(profile?.data?.data?.roleId) == 10 || Number(profile?.data?.data?.roleId) == 10 ||
Number(profile?.data?.data?.roleId) == 11 || Number(profile?.data?.data?.roleId) == 11 ||
Number(profile?.data?.data?.roleId) == 12 || Number(profile?.data?.data?.roleId) == 12 ||
Number(profile?.data?.data?.roleId) == 18 Number(profile?.data?.data?.roleId) == 18 ||
Number(profile?.data?.data?.roleId) == 19
) { ) {
if (profile?.data?.data?.roleId === 18) { if (profile?.data?.data?.roleId === 18) {
window.location.href = "/in/dashboard/executive"; window.location.href = "/in/dashboard/executive";

View File

@ -81,7 +81,7 @@ const ProfileInfo = () => {
<div className="text-sm font-medium capitalize lg:block hidden"> <div className="text-sm font-medium capitalize lg:block hidden">
{detail?.fullname} {detail?.fullname}
</div> </div>
<p className="text-xs">({detail?.fullname})</p> <p className="text-xs">({detail?.username})</p>
</div> </div>
<span className="text-base me-2.5 lg:inline-block hidden"> <span className="text-base me-2.5 lg:inline-block hidden">
<Icon icon="heroicons-outline:chevron-down"></Icon> <Icon icon="heroicons-outline:chevron-down"></Icon>

View File

@ -0,0 +1,285 @@
"use client"
import * as React from "react"
import { ChevronsUpDown, Check, CirclePlus } from 'lucide-react';
import { cn, getCookiesDecrypt } from "@/lib/utils"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { useConfig } from "@/hooks/use-config";
import { useMediaQuery } from "@/hooks/use-media-query";
import { motion, AnimatePresence } from "framer-motion";
import { useMenuHoverConfig } from "@/hooks/use-menu-hover";
import { getInfoProfile } from "@/service/auth";
import { getUserRolePlacements, saveUserRolePlacements } from "@/service/management-user/management-user";
// var groups = [
// {
// label: "Wilayah Tugas",
// teams: [],
// },
// ]
// type Team = (typeof groups)[number]["teams"][number]
type PopoverTriggerProps = React.ComponentPropsWithoutRef<typeof PopoverTrigger>
interface TeamSwitcherProps extends PopoverTriggerProps { }
const scaleVariants = {
collapsed: { scale: 0.8 },
expanded: { scale: 1 }
};
export default function TeamWorkspaceSwitcher({ className }: TeamSwitcherProps) {
const [config] = useConfig();
const [hoverConfig] = useMenuHoverConfig();
const { hovered } = hoverConfig;
const [detail, setDetail] = React.useState<any>();
const userId = getCookiesDecrypt("uie");
const [open, setOpen] = React.useState(false);
const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false);
const [groups, setGroups] = React.useState<any>()
const [selectedTeam, setSelectedTeam] = React.useState<any>({ label: "", value: "" });
const isDesktop = useMediaQuery("(min-width: 1280px)")
if (config.showSwitcher === false || config.sidebar === 'compact') return null
React.useEffect(() => {
async function initState() {
const response = await getInfoProfile();
const details = response?.data?.data;
setDetail(details);
console.log("data", details);
}
async function getPlacement() {
const response = await getUserRolePlacements(Number(userId));
const data = response?.data?.data;
var placementArr: any[] = [];
data?.forEach((row: any) => {
placementArr.push({
label: row.roleName + " | " + row.userLevelName,
value: Number(row.id),
});
});
const groupsTemp = [
{
label: "Wilayah Tugas",
teams: placementArr,
}
];
setGroups(groupsTemp);
}
initState();
getPlacement();
}, []);
return (
<Dialog open={showNewTeamDialog} onOpenChange={setShowNewTeamDialog}>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<motion.div
key={(config.collapsed && !hovered) ? "collapsed" : "expanded"}
initial={{ scale: 0.9 }}
animate={{ scale: 1 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
{(config.collapsed && !hovered) ? <Button
variant="outline"
color="secondary"
role="combobox"
fullWidth
aria-expanded={open}
aria-label="Select a team"
className={cn(" h-14 w-14 mx-auto p-0 md:p-0 dark:border-secondary ring-offset-sidebar", className)}
>
<Avatar className="">
<AvatarImage
height={24}
width={24}
// src={session?.user?.image as any}
alt={selectedTeam.label}
className="grayscale"
/>
<AvatarFallback>{detail?.username}</AvatarFallback>
</Avatar>
</Button> : <Button
variant="outline"
color="secondary"
role="combobox"
fullWidth
aria-expanded={open}
aria-label="Select a team"
className={cn(" h-auto py-3 md:px-3 px-3 justify-start dark:border-secondary ring-offset-sidebar", className)}
>
<div className=" flex gap-2 flex-1 items-center">
<Avatar className=" flex-none h-[38px] w-[38px]">
{/* <AvatarImage
height={38}
width={38}
// src={session?.user?.image as any}
alt={selectedTeam.label}
className="grayscale"
/> */}
<AvatarFallback><p className="text-md uppercase">{detail?.username[0]}</p></AvatarFallback>
</Avatar>
<div className="flex-1 text-start w-[100px]">
<div className=" text-sm font-semibold text-default-900">{detail?.username}</div>
<div className=" text-xs font-normal text-default-500 dark:text-default-700 truncate ">{selectedTeam.label}</div>
</div>
<div className="">
<ChevronsUpDown className="ml-auto h-5 w-5 shrink-0 text-default-500 dark:text-default-700" />
</div>
</div>
</Button>}
</motion.div>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandList>
<CommandInput placeholder="Search team..." className=" placeholder:text-xs" />
<CommandEmpty>No team found.</CommandEmpty>
{groups?.map((group: any) => (
<CommandGroup key={group.label} heading={group.label}>
{group.teams.map((team: any) => (
<CommandItem
key={team.value}
onSelect={() => {
setSelectedTeam(team)
setOpen(false)
}}
className="text-sm font-normal"
>
{team.label}
<Check
className={cn(
"ml-auto h-4 w-4",
selectedTeam.value === team.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
{/* <CommandSeparator /> */}
{/* <CommandList>
<CommandGroup>
<DialogTrigger asChild>
<CommandItem
onSelect={() => {
setOpen(false)
setShowNewTeamDialog(true)
}}
>
<CirclePlus className="mr-2 h-5 w-5" />
Create Team
</CommandItem>
</DialogTrigger>
</CommandGroup>
</CommandList> */}
</Command>
</PopoverContent>
</Popover>
<DialogContent>
<DialogHeader>
<DialogTitle>Create team</DialogTitle>
<DialogDescription>
Add a new team to manage products and customers.
</DialogDescription>
</DialogHeader>
<div>
<div className="space-y-4 py-2 pb-4">
<div className="space-y-2">
<Label htmlFor="name">Team name</Label>
<Input id="name" placeholder="Acme Inc." />
</div>
<div className="space-y-2">
<Label htmlFor="plan">Subscription plan</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a plan" />
</SelectTrigger>
<SelectContent>
<SelectItem value="free">
<span className="font-medium">Free</span> -{" "}
<span className="text-muted-foreground">
Trial for two weeks
</span>
</SelectItem>
<SelectItem value="pro">
<span className="font-medium">Pro</span> -{" "}
<span className="text-muted-foreground">
$9/month per user
</span>
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowNewTeamDialog(false)}>
Cancel
</Button>
<Button type="submit">Continue</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}

View File

@ -3,7 +3,7 @@
import React from 'react' import React from 'react'
import { Ellipsis, LogOut } from "lucide-react"; import { Ellipsis, LogOut } from "lucide-react";
import { usePathname } from "@/components/navigation"; import { usePathname } from "@/components/navigation";
import { cn } from "@/lib/utils"; import { cn, getCookiesDecrypt } from "@/lib/utils";
import { getMenuList } from "@/lib/menus"; import { getMenuList } from "@/lib/menus";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { import {
@ -26,6 +26,7 @@ import Logo from '@/components/logo';
import SidebarHoverToggle from '@/components/partials/sidebar/sidebar-hover-toggle'; import SidebarHoverToggle from '@/components/partials/sidebar/sidebar-hover-toggle';
import { useMenuHoverConfig } from '@/hooks/use-menu-hover'; import { useMenuHoverConfig } from '@/hooks/use-menu-hover';
import { useMediaQuery } from '@/hooks/use-media-query'; import { useMediaQuery } from '@/hooks/use-media-query';
import TeamWorkspaceSwitcher from '../common/team-workspace-switcher';
export function MenuClassic({ }) { export function MenuClassic({ }) {
@ -36,7 +37,7 @@ export function MenuClassic({ }) {
const direction = getLangDir(params?.locale ?? ''); const direction = getLangDir(params?.locale ?? '');
const isDesktop = useMediaQuery('(min-width: 1280px)') const isDesktop = useMediaQuery('(min-width: 1280px)')
const userRoleId = getCookiesDecrypt("urie");
const menuList = getMenuList(pathname, t); const menuList = getMenuList(pathname, t);
const [config, setConfig] = useConfig() const [config, setConfig] = useConfig()
@ -71,17 +72,16 @@ export function MenuClassic({ }) {
<ScrollArea className="[&>div>div[style]]:!block" dir={direction}> <ScrollArea className="[&>div>div[style]]:!block" dir={direction}>
{/* {isDesktop && ( {isDesktop && Number(userRoleId) == 19 ? (
<div className={cn(' space-y-3 mt-6 ', { <div className={cn(' space-y-3 mt-6 ', {
'px-4': !collapsed || hovered, 'px-4': !collapsed || hovered,
'text-center': collapsed || !hovered 'text-center': collapsed || !hovered
})}> })}>
<TeamWorkspaceSwitcher />
<TeamSwitcher /> {/* <SearchBar /> */}
<SearchBar />
</div> </div>
)} */} ) : ""}
<nav className="mt-4 h-full w-full"> <nav className="mt-4 h-full w-full">
<ul className=" h-full flex flex-col min-h-[calc(100vh-48px-36px-16px-32px)] lg:min-h-[calc(100vh-32px-40px-32px)] items-start space-y-1 px-4"> <ul className=" h-full flex flex-col min-h-[calc(100vh-48px-36px-16px-32px)] lg:min-h-[calc(100vh-32px-40px-32px)] items-start space-y-1 px-4">

View File

@ -27,7 +27,7 @@ export default function PerformancePoldaViz() {
? isInternational[0] ? isInternational[0]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-top10?" ? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-top10?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-ranking-polda?" : "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-ranking-polda?"
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?`; : `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${provState}&`;
const view2 = const view2 =
levelName == "MABES POLRI" levelName == "MABES POLRI"

View File

@ -0,0 +1,84 @@
"use client";
import Cookies from "js-cookie";
import { useEffect, useState } from "react";
import { getCookiesDecrypt } from "@/lib/utils";
import { generateTicket } from "@/service/tableau/tableau-service";
import { Button } from "../ui/button";
import { useTranslations } from "next-intl";
export default function PerformanceSatkerViz() {
const [hasMounted, setHasMounted] = useState(false);
const t = useTranslations("AnalyticsDashboard");
const levelName = getCookiesDecrypt("ulnae");
const poldaState = Cookies.get("state");
const provState = Cookies.get("state-prov");
const [ticket1, setTicket1] = useState("");
const [ticket2, setTicket2] = useState("");
const [ticket3, setTicket3] = useState("");
const [ticket4, setTicket4] = useState("");
const [isInternational, setIsInternational] = useState([false, false, false]);
const baseUrl = "https://db-mediahub.polri.go.id/";
const url = "https://db-mediahub.polri.go.id/trusted/";
const view1 =
levelName == "MABES POLRI"
? isInternational[0]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-top10?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-top10?"
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${provState}&`;
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
useEffect(() => {
async function initState() {
const response1 = await generateTicket();
setTicket1(response1?.data?.data);
}
initState();
}, [isInternational]);
// Hooks
useEffect(() => {
setHasMounted(true);
}, []);
// Render
if (!hasMounted) return null;
const handleInternational = (index: number, val: boolean) => {
const updatedIsInternational = [...isInternational];
updatedIsInternational[index] = val;
setIsInternational(updatedIsInternational);
};
return (
<div className="flex flex-col gap-2 bg-white rounded-lg p-3">
<p className="text-lg">
<b>
{isInternational[0] ? "SATKER PERFORMANCE" : "POLFORMANCE SATKER"}
</b>
</p>
<div className="my-5">
{ticket1 == "" ? (
<iframe
src={`${baseUrl + view1 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket1}/${view1}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</div>
);
}

View File

@ -1,3 +1,4 @@
import { Router } from "next/router";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
@ -29,7 +30,7 @@ export function error(msg: string): void {
}); });
} }
export function success(redirect: string): void { export function success(router: Router, redirect: string): void {
MySwal.fire({ MySwal.fire({
title: '<p class="text-green-600 font-bold">Sukses</p>', title: '<p class="text-green-600 font-bold">Sukses</p>',
icon: "success", icon: "success",
@ -38,7 +39,7 @@ export function success(redirect: string): void {
allowOutsideClick: false, allowOutsideClick: false,
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
window.location.href = redirect; router.push(redirect);
} }
}); });
} }

View File

@ -260,6 +260,70 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
], ],
}, },
{
groupLabel: "",
id: "settings",
menus: [
{
id: "settings",
href: "/admin/settings",
label: t("settings"),
active: pathname.includes("/settinng"),
icon: "material-symbols:settings",
submenus: [
{
href: "/admin/settings/category",
label: t("category"),
active: pathname === "/admin/settings/category",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/tag",
label: "Tag",
active: pathname === "/admin/settings/tag",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/banner",
label: "Banner",
active: pathname === "/admin/settings/banner",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/feedback",
label: "Feedback",
active: pathname === "/admin/settings/feedback",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/faq",
label: "FAQ",
active: pathname === "/admin/settings/faq",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "https://nat-mediahub.polri.go.id/",
label: "Mediahub 2022",
active: pathname === "/admin/settings/mediahub-2022",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/privacy",
label: t("privacy"),
active: pathname === "/admin/settings/privacy",
icon: "heroicons:arrow-trending-up",
children: [],
},
],
},
],
},
// ===== MENU TEMPLATE DASHCODE ===== // ===== MENU TEMPLATE DASHCODE =====
// { // {
// groupLabel: t("apps"), // groupLabel: t("apps"),
@ -1527,21 +1591,21 @@ export function getMenuList(pathname: string, t: any): Group[] {
submenus: [ submenus: [
{ {
href: "/contributor/schedule/press-conference", href: "/contributor/schedule/press-conference",
label: "konferensi pers", label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"), active: pathname.includes("/schedule/press-conference"),
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/event", href: "/contributor/schedule/event",
label: "event", label: t("event"),
active: pathname.includes("/schedule/event"), active: pathname.includes("/schedule/event"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/press-release", href: "/contributor/schedule/press-release",
label: "pers rilis", label: t("press-release"),
active: pathname.includes("/schedule/press-release"), active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
@ -1725,21 +1789,21 @@ export function getMenuList(pathname: string, t: any): Group[] {
submenus: [ submenus: [
{ {
href: "/contributor/schedule/press-conference", href: "/contributor/schedule/press-conference",
label: "konferensi pers", label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"), active: pathname.includes("/schedule/press-conference"),
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/event", href: "/contributor/schedule/event",
label: "event", label: t("event"),
active: pathname.includes("/schedule/event"), active: pathname.includes("/schedule/event"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/press-release", href: "/contributor/schedule/press-release",
label: "pers rilis", label: t("press-release"),
active: pathname.includes("/schedule/press-release"), active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
@ -1790,6 +1854,70 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
], ],
}, },
{
groupLabel: "",
id: "settings",
menus: [
{
id: "settings",
href: "/admin/settings",
label: t("settings"),
active: pathname.includes("/settinng"),
icon: "material-symbols:settings",
submenus: [
{
href: "/admin/settings/category",
label: t("category"),
active: pathname === "/admin/settings/category",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/tag",
label: "Tag",
active: pathname === "/admin/settings/tag",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/banner",
label: "Banner",
active: pathname === "/admin/settings/banner",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/feedback",
label: "Feedback",
active: pathname === "/admin/settings/feedback",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/faq",
label: "FAQ",
active: pathname === "/admin/settings/faq",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "https://nat-mediahub.polri.go.id/",
label: "Mediahub 2022",
active: pathname === "/admin/settings/mediahub-2022",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/privacy",
label: t("privacy"),
active: pathname === "/admin/settings/privacy",
icon: "heroicons:arrow-trending-up",
children: [],
},
],
},
],
},
]; ];
} else if (Number(userLevelId) == 761) { } else if (Number(userLevelId) == 761) {
menusSelected = [ menusSelected = [
@ -1891,7 +2019,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
submenus: [ submenus: [
{ {
href: "/contributor/schedule/press-conference", href: "/contributor/schedule/press-conference",
label: "konferensi pers", label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"), active: pathname.includes("/schedule/press-conference"),
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
@ -1905,7 +2033,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
{ {
href: "/contributor/schedule/press-release", href: "/contributor/schedule/press-release",
label: "pers rilis", label: t("press-release"),
active: pathname.includes("/schedule/press-release"), active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
@ -2105,21 +2233,21 @@ export function getMenuList(pathname: string, t: any): Group[] {
submenus: [ submenus: [
{ {
href: "/contributor/schedule/press-conference", href: "/contributor/schedule/press-conference",
label: "konferensi pers", label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"), active: pathname.includes("/schedule/press-conference"),
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/event", href: "/contributor/schedule/event",
label: "event", label: t("event"),
active: pathname.includes("/schedule/event"), active: pathname.includes("/schedule/event"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/press-release", href: "/contributor/schedule/press-release",
label: "pers rilis", label: t("press-release"),
active: pathname.includes("/schedule/press-release"), active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
@ -2314,21 +2442,21 @@ export function getMenuList(pathname: string, t: any): Group[] {
submenus: [ submenus: [
{ {
href: "/contributor/schedule/press-conference", href: "/contributor/schedule/press-conference",
label: "konferensi pers", label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"), active: pathname.includes("/schedule/press-conference"),
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/event", href: "/contributor/schedule/event",
label: "event", label: t("event"),
active: pathname.includes("/schedule/event"), active: pathname.includes("/schedule/event"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
}, },
{ {
href: "/contributor/schedule/press-release", href: "/contributor/schedule/press-release",
label: "pers rilis", label: t("press-release"),
active: pathname.includes("/schedule/press-release"), active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart", icon: "heroicons:shopping-cart",
children: [], children: [],
@ -2533,7 +2661,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
], ],
}, },
]; ];
} else if (Number(roleId) == 11) { } else if (Number(roleId) == 11 || Number(roleId) == 19) {
menusSelected = [ menusSelected = [
{ {
groupLabel: t("apps"), groupLabel: t("apps"),
@ -2706,16 +2834,16 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "material-symbols:dashboard", icon: "material-symbols:dashboard",
submenus: [ submenus: [
{ {
href: "/dashboard", href: "/dashboard/executive",
label: "Breakdown", label: "Executive",
active: pathname === "/dashboard", active: pathname === "/dashboard/executive",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{ {
href: "/dashboard/executive", href: "/dashboard",
label: "Executive", label: "Breakdown",
active: pathname === "/dashboard/executive", active: pathname === "/dashboard",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
@ -2723,20 +2851,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
], ],
}, },
{ // {
groupLabel: "", // groupLabel: "",
id: "agenda-setting", // id: "agenda-setting",
menus: [ // menus: [
{ // {
id: "agenda-setting", // id: "agenda-setting",
href: "/contributor/agenda-setting", // href: "/contributor/agenda-setting",
label: t("agenda-setting"), // label: t("agenda-setting"),
active: pathname.includes("/agenda-setting"), // active: pathname.includes("/agenda-setting"),
icon: "iconoir:journal-page", // icon: "iconoir:journal-page",
submenus: [], // submenus: [],
}, // },
], // ],
}, // },
{ {
groupLabel: "", groupLabel: "",
id: "management-user", id: "management-user",
@ -3015,16 +3143,16 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "material-symbols:dashboard", icon: "material-symbols:dashboard",
submenus: [ submenus: [
{ {
href: "/dashboard", href: "/dashboard/executive",
label: "Breakdown", label: "Executive",
active: pathname === "/dashboard", active: pathname === "/dashboard/executive",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{ {
href: "/dashboard/executive", href: "/dashboard",
label: "Executive", label: "Breakdown",
active: pathname === "/dashboard/executive", active: pathname === "/dashboard",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
@ -3074,20 +3202,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
], ],
}, },
{ // {
groupLabel: "", // groupLabel: "",
id: "agenda-setting", // id: "agenda-setting",
menus: [ // menus: [
{ // {
id: "agenda-setting", // id: "agenda-setting",
href: "/contributor/agenda-setting", // href: "/contributor/agenda-setting",
label: t("agenda-setting"), // label: t("agenda-setting"),
active: pathname.includes("/agenda-setting"), // active: pathname.includes("/agenda-setting"),
icon: "iconoir:journal-page", // icon: "iconoir:journal-page",
submenus: [], // submenus: [],
}, // },
], // ],
}, // },
{ {
groupLabel: "", groupLabel: "",
id: "performance-polres", id: "performance-polres",
@ -3159,35 +3287,70 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
], ],
}, },
// { {
// groupLabel: "", groupLabel: "",
// id: "settings", id: "settings",
// menus: [ menus: [
// { {
// id: "settings", id: "settings",
// href: "/admin/settings", href: "/admin/settings",
// label: t("settings"), label: t("settings"),
// active: pathname.includes("/settinng"), active: pathname.includes("/settinng"),
// icon: "material-symbols:settings", icon: "material-symbols:settings",
// submenus: [ submenus: [
// { {
// href: "/admin/settings/category", href: "/admin/settings/category",
// label: t("category"), label: t("category"),
// active: pathname === "/admin/settings/category", active: pathname === "/admin/settings/category",
// icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
// children: [], children: [],
// }, },
// { {
// href: "/admin/settings/tag", href: "/admin/settings/tag",
// label: "Tag", label: "Tag",
// active: pathname === "/admin/settings/tag", active: pathname === "/admin/settings/tag",
// icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
// children: [], children: [],
// }, },
// ], {
// }, href: "/admin/settings/banner",
// ], label: "Banner",
// }, active: pathname === "/admin/settings/banner",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/feedback",
label: "Feedback",
active: pathname === "/admin/settings/feedback",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/faq",
label: "FAQ",
active: pathname === "/admin/settings/faq",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "https://nat-mediahub.polri.go.id/",
label: "Mediahub 2022",
active: pathname === "/admin/settings/mediahub-2022",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/privacy",
label: t("privacy"),
active: pathname === "/admin/settings/privacy",
icon: "heroicons:arrow-trending-up",
children: [],
},
],
},
],
},
]; ];
} else { } else {
menusSelected = [ menusSelected = [
@ -3201,11 +3364,39 @@ export function getMenuList(pathname: string, t: any): Group[] {
label: t("dashboard"), label: t("dashboard"),
active: pathname.includes("/dashboard"), active: pathname.includes("/dashboard"),
icon: "material-symbols:dashboard", icon: "material-symbols:dashboard",
submenus: [
{
href: "/dashboard/executive",
label: "Executive",
active: pathname === "/dashboard/executive",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/dashboard",
label: "Breakdown",
active: pathname === "/dashboard",
icon: "heroicons:arrow-trending-up",
children: [],
},
],
},
],
},
{
groupLabel: "",
id: "management-user",
menus: [
{
id: "management-user-menu",
href: "/admin/management-user",
label: "Management User",
active: pathname.includes("/management-user"),
icon: "clarity:users-solid",
submenus: [], submenus: [],
}, },
], ],
}, },
{ {
groupLabel: "", groupLabel: "",
id: "content-production", id: "content-production",
@ -3234,6 +3425,49 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
], ],
}, },
{
groupLabel: "",
id: "performance-polres",
menus: [
{
id: "performance-polres",
href: "/admin/performance-satker",
label: t("performance-satker"),
active: pathname.includes("/admin/performance-satker"),
icon: "ant-design:signal-filled",
submenus: [],
},
],
},
{
groupLabel: "",
id: "media-tracking",
menus: [
{
id: "media-tracking",
href: "/curator/media-tracking",
label: t("media-tracking"),
active: pathname.includes("/media-tracking"),
icon: "material-symbols:map-search-outline",
submenus: [
{
href: "/admin/media-tracking/media-online",
label: "Media Online",
active: pathname === "/media-tracking/media-online",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/media-tracking/tb-news",
label: "Tracking Beritra Hari Ini",
active: pathname === "/media-tracking/news",
icon: "heroicons:arrow-trending-up",
children: [],
},
],
},
],
},
{ {
groupLabel: "", groupLabel: "",
id: "communication", id: "communication",
@ -3273,6 +3507,41 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{
href: "/admin/settings/banner",
label: "Banner",
active: pathname === "/admin/settings/banner",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/feedback",
label: "Feedback",
active: pathname === "/admin/settings/feedback",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/faq",
label: "FAQ",
active: pathname === "/admin/settings/faq",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "https://nat-mediahub.polri.go.id/",
label: "Mediahub 2022",
active: pathname === "/admin/settings/mediahub-2022",
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/admin/settings/privacy",
label: t("privacy"),
active: pathname === "/admin/settings/privacy",
icon: "heroicons:arrow-trending-up",
children: [],
},
], ],
}, },
], ],

View File

@ -325,10 +325,15 @@
"colors": "Colors", "colors": "Colors",
"performance-polda": "Performance Polda", "performance-polda": "Performance Polda",
"performance-polres": "Performance Polres", "performance-polres": "Performance Polres",
"performance-satker": "Performance Satker",
"analysis": "Analysis", "analysis": "Analysis",
"management-content": "Content Management ", "management-content": "Content Management ",
"add-experts": "Add Experts", "add-experts": "Add Experts",
"category": "Category", "category": "Category",
"add-category": "Add Category",
"tags": "Tags",
"add-tags": "Add Tags",
"add": "Add",
"privacy": "Privacy Policy" "privacy": "Privacy Policy"
}, },
"Changelog": { "Changelog": {
@ -580,7 +585,8 @@
"schedule": "Schedule", "schedule": "Schedule",
"press-conference": "Press Conference", "press-conference": "Press Conference",
"press-release": "Press Release", "press-release": "Press Release",
"create-schedule": "Create Schedule" "create-schedule": "Create Schedule",
"event": "event"
}, },
"Blog": { "Blog": {
"table": "Table", "table": "Table",
@ -599,5 +605,37 @@
"table": "Table", "table": "Table",
"contest": "Lomba", "contest": "Lomba",
"create-contest": "Create Contest" "create-contest": "Create Contest"
},
"Curation": {
"content-curation": "Content Curation"
},
"Table": {
"no": "No",
"title": "Title",
"category-name": "Category Name",
"upload-date": "Upload Date",
"creator-group": "Creator Group",
"source": "source",
"published": "Published",
"date": "Date",
"category": "Category",
"tag": "Tag",
"type-content": "Content Type",
"type-task": "Task Type",
"category-task": "Category Task",
"code": "Code",
"start-date": "Start Date",
"end-date": "End Date",
"speaker": "Speaker",
"time": "Time",
"address": "Address",
"question": "Question",
"sender": "Created By",
"sendto": "SendTo",
"type": "Type",
"duration": "Duration",
"target-output": "Target Output",
"target-participant": "Target Participant",
"action": "Actions"
} }
} }

View File

@ -326,10 +326,15 @@
"colors": "Colors", "colors": "Colors",
"performance-polda": "Performa Polda", "performance-polda": "Performa Polda",
"performance-polres": "Performa Polres", "performance-polres": "Performa Polres",
"performance-satker": "Performa Satker",
"analysis": "Analisa", "analysis": "Analisa",
"management-content": "Manajemen Konten", "management-content": "Manajemen Konten",
"add-experts": "Tambah Tenaga Ahli", "add-experts": "Tambah Tenaga Ahli",
"category": "Kategori", "category": "Kategori",
"add-category": "Tambah Kategori",
"tags": "Tag",
"add-tags": "Tambah Tag",
"add": "Tambah",
"privacy": "Kebijakan Privacy" "privacy": "Kebijakan Privacy"
}, },
"Changelog": { "Changelog": {
@ -581,7 +586,8 @@
"schedule": "Jadwal", "schedule": "Jadwal",
"press-conference": "Konferensi Pers", "press-conference": "Konferensi Pers",
"press-release": "Pers Rilis", "press-release": "Pers Rilis",
"create-schedule": "Buat Jadwal" "create-schedule": "Buat Jadwal",
"event": "event"
}, },
"Blog": { "Blog": {
"table": "Tabel", "table": "Tabel",
@ -600,5 +606,37 @@
"table": "Tabel", "table": "Tabel",
"contest": "Lomba", "contest": "Lomba",
"create-contest": "Buat Lomba" "create-contest": "Buat Lomba"
},
"Curation": {
"content-curation": "Kurasi Konten"
},
"Table": {
"no": "Nomor",
"title": "Judul",
"category-name": "Nama Kategori",
"upload-date": "Tanggal Upload",
"creator-group": "Pembuat",
"source": "Sumber",
"published": "Diterbitkan",
"date": "Tanggal",
"category": "Kategori",
"tag": "Tag",
"type-content": "Tipe Konten",
"type-task": "Tipen Penugasan",
"category-task": "Kategori Penugasan",
"code": "Kode",
"start-date": "Tanggal Mulai",
"end-date": "Tanggal Selesai",
"speaker": "Disampaikan Oleh",
"time": "Waktu",
"address": "Alamat",
"question": "Pertanyaan",
"sender": "Pengirim",
"sendto": "Penerima",
"type": "Tipe",
"duration": "Durasi",
"target-output": "Target Output",
"target-participant": "Target Peserta",
"action": "Aksi"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -129,10 +129,11 @@ export async function listSPIT(
page: any, page: any,
limit: any, limit: any,
title = "", title = "",
contentCreatedDate = "",
isPublish: any isPublish: any
) { ) {
return await httpGetInterceptor( return await httpGetInterceptor(
`media/spit/pagination?enablePage=1&page=${page}&size=${limit}&sort=desc&sortBy=contentTitleId&title=${title}&isPublish=${isPublish}` `media/spit/pagination?enablePage=1&page=${page}&size=${limit}&sort=desc&sortBy=contentTitleId&title=${title}& contentCreatedDate=${contentCreatedDate}&isPublish=${isPublish}`
); );
} }
@ -154,7 +155,7 @@ export async function listEnableCategory(type: any) {
} }
export async function listCategory(type: any) { export async function listCategory(type: any) {
const url = `media/categories/list/publish?enablePage=0&sort=desc&sortBy=id&categoryId=${type}`; const url = `media/categories/list?enablePage=0&sort=desc&sortBy=id&categoryId=${type}`;
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }

View File

@ -0,0 +1,21 @@
import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
export async function listDataExperts(size: number, page: number) {
return await httpGetInterceptor(
`users/pagination/internal?enablePage=1&size=${size}&page=${page}&roleId=19&levelId=1`
);
}
export async function postBlog(data: any) {
const url = "blog";
return httpPostInterceptor(url, data);
}
export async function getBlog(id: any) {
const url = `blog/${id}`;
return httpGetInterceptor(url);
}

View File

@ -0,0 +1,13 @@
import axios from "axios";
const baseURL = "https://mediahub.polri.go.id/api/";
const axiosBaseProdInstance = axios.create({
baseURL,
headers: {
"content-type": "application/json",
},
withCredentials: false,
});
export default axiosBaseProdInstance;

View File

@ -17,7 +17,6 @@ const axiosInterceptorInstance = axios.create({
// Request interceptor // Request interceptor
axiosInterceptorInstance.interceptors.request.use( axiosInterceptorInstance.interceptors.request.use(
(config) => { (config) => {
console.log("Config interceptor : ", config);
const accessToken = Cookies.get("access_token"); const accessToken = Cookies.get("access_token");
if (accessToken) { if (accessToken) {
if (config.headers) if (config.headers)

View File

@ -0,0 +1,171 @@
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { getCsrfToken } from "../auth";
import axiosBaseProdInstance from "./axios-base-prod-instance";
export async function httpGetInterceptor(pathUrl: any) {
const pathname = window.location.pathname;
const response = await axiosBaseProdInstance
.get(pathUrl)
.catch((error) => error.response);
console.log("Response interceptor : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else if (response?.status == 401) {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
if (
pathname?.includes("/contributor/") ||
pathname?.includes("/admin/") ||
pathname?.includes("/supervisor/")
) {
window.location.href = "/";
}
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
export async function httpPostInterceptor(
pathUrl: any,
data?: any,
headers?: any
) {
const resCsrf = await getCsrfToken();
const defaultHeaders = {
"Content-Type": "application/json",
};
const mergedHeaders = {
...defaultHeaders,
...headers,
};
const response = await axiosBaseProdInstance
.post(pathUrl, data, { headers: mergedHeaders })
.catch((error) => error.response);
console.log("Response interceptor : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else if (response?.status == 401) {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
window.location.href = "/";
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
export async function httpPutInterceptor(
pathUrl: any,
data: any,
headers?: any
) {
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
const defaultHeaders = {
"Content-Type": "application/json",
};
const mergedHeaders = {
...defaultHeaders,
...headers,
};
const response = await axiosBaseProdInstance
.put(pathUrl, data, { headers: mergedHeaders })
.catch((error) => error.response);
console.log("Response interceptor : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else if (response?.status == 401) {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
window.location.href = "/";
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
export async function httpDeleteInterceptor(pathUrl: any, data?: any) {
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
const defaultHeaders = {
"Content-Type": "application/json",
};
const mergedHeaders = {
...defaultHeaders,
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
};
const response = await axiosBaseProdInstance
.delete(pathUrl, { headers: mergedHeaders, data })
.catch((error) => error.response);
console.log("Response interceptor : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else if (response?.status == 401) {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
window.location.href = "/";
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
export async function httpGetInterceptorWithToken(pathUrl: any, headers?: any) {
const response = await axiosBaseProdInstance
.get(pathUrl, headers)
.catch((error) => error.response);
console.log("Response interceptor : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}

View File

@ -30,16 +30,32 @@ export async function getListSchools() {
const url = "users/user-schools/list"; const url = "users/user-schools/list";
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function getListCompetencies() { export async function getListCompetencies() {
const url = "users/user-competencies/list"; const url = "users/user-competencies/list";
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function getListExperiences() {
const url = "users/user-experiences/list";
return httpGetInterceptor(url);
}
export async function saveUserInternal(data: any) { export async function saveUserInternal(data: any) {
const url = "users/save"; const url = "users/save";
return httpPostInterceptor(url, data); return httpPostInterceptor(url, data);
} }
export async function saveUserRolePlacements(data: any) {
const url = "users/role-placements";
return httpPostInterceptor(url, data);
}
export async function getUserRolePlacements(userId: number) {
const url = `users/role-placements?userId=${userId}`;
return httpGetInterceptor(url);
}
export async function getUserById(id: string) { export async function getUserById(id: string) {
const url = `users?id=${id}`; const url = `users?id=${id}`;
return httpGetInterceptor(url); return httpGetInterceptor(url);

View File

@ -5,8 +5,15 @@ import {
import { httpGet } from "../http-config/http-base-service"; import { httpGet } from "../http-config/http-base-service";
import { any } from "zod"; import { any } from "zod";
export async function paginationSchedule(size: number, page: number, type: any, title: string = "") { export async function paginationSchedule(
return await httpGetInterceptor(`schedule/pagination?enablePage=1&scheduleTypeId=${type}&page=${page}&size=${size}&title=${title}`); size: number,
page: number,
type: any,
title: string = ""
) {
return await httpGetInterceptor(
`schedule/pagination?enablePage=1&scheduleTypeId=${type}&page=${page}&size=${size}&title=${title}`
);
} }
export async function postSchedule(data: any) { export async function postSchedule(data: any) {
@ -49,6 +56,11 @@ export async function listScheduleToday() {
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function getListScheduleAttachment(scheduleId: any) {
const url = `media/list?&enablePage=0&scheduleId=${scheduleId}`;
return httpGetInterceptor(url);
}
export async function listScheduleNext() { export async function listScheduleNext() {
const url = "schedule/next-activity"; const url = "schedule/next-activity";
return httpGetInterceptor(url); return httpGetInterceptor(url);

View File

@ -1,4 +1,4 @@
import { httpPostInterceptor } from "../http-config/http-interceptor-service"; import { httpPostInterceptor } from "../http-config/http-interceptor-prod-service";
export async function generateTicket() { export async function generateTicket() {
const url = "/admin/tableau-ticket"; const url = "/admin/tableau-ticket";