test
This commit is contained in:
commit
0fe7721eb6
|
|
@ -17,7 +17,8 @@ COPY package.json pnpm-lock.yaml ./
|
||||||
COPY vendor/ckeditor5 ./vendor/ckeditor5
|
COPY vendor/ckeditor5 ./vendor/ckeditor5
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN pnpm install --frozen-lockfile
|
RUN pnpm install
|
||||||
|
# RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
# Menyalin source code aplikasi
|
# Menyalin source code aplikasi
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@ const columns: ColumnDef<any>[] = [
|
||||||
cell: ({ row }) => <span>{row.getValue("experience")}</span>,
|
cell: ({ row }) => <span>{row.getValue("experience")}</span>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "experience",
|
||||||
|
header: "Posisi",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("experience")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
accessorKey: "action",
|
accessorKey: "action",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,13 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { AdministrationLevelList, getListCompetencies, getListExperiences, saveUserInternal, saveUserRolePlacements } from "@/service/management-user/management-user";
|
import {
|
||||||
|
AdministrationLevelList,
|
||||||
|
getListCompetencies,
|
||||||
|
getListExperiences,
|
||||||
|
saveUserInternal,
|
||||||
|
saveUserRolePlacements,
|
||||||
|
} from "@/service/management-user/management-user";
|
||||||
import { loading } from "@/config/swal";
|
import { loading } from "@/config/swal";
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
|
|
@ -58,7 +64,7 @@ export type Placements = {
|
||||||
index: number;
|
index: number;
|
||||||
roleId?: string;
|
roleId?: string;
|
||||||
userLevelId?: number;
|
userLevelId?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function AddExpertForm() {
|
export default function AddExpertForm() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
@ -67,7 +73,9 @@ export default function AddExpertForm() {
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema),
|
||||||
});
|
});
|
||||||
const [incrementId, setIncrementId] = useState(1);
|
const [incrementId, setIncrementId] = useState(1);
|
||||||
const [placementRows, setPlacementRows] = useState<Placements[]>([{ index: 0, roleId: "", userLevelId: 0 }]);
|
const [placementRows, setPlacementRows] = useState<Placements[]>([
|
||||||
|
{ index: 0, roleId: "", userLevelId: 0 },
|
||||||
|
]);
|
||||||
const [userCompetencies, setUserCompetencies] = useState<any>();
|
const [userCompetencies, setUserCompetencies] = useState<any>();
|
||||||
const [userExperiences, setUserExperiences] = useState<any>();
|
const [userExperiences, setUserExperiences] = useState<any>();
|
||||||
const [userLevels, setUserLevels] = useState<any>();
|
const [userLevels, setUserLevels] = useState<any>();
|
||||||
|
|
@ -80,7 +88,7 @@ export default function AddExpertForm() {
|
||||||
{
|
{
|
||||||
id: "12",
|
id: "12",
|
||||||
name: "Kurator",
|
name: "Kurator",
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
|
@ -113,12 +121,12 @@ export default function AddExpertForm() {
|
||||||
userCompetencyId: data.skills,
|
userCompetencyId: data.skills,
|
||||||
userExperienceId: data.experiences,
|
userExperienceId: data.experiences,
|
||||||
companyName: data.company,
|
companyName: data.company,
|
||||||
}
|
};
|
||||||
|
|
||||||
loading();
|
loading();
|
||||||
const res = await saveUserInternal(dataReq);
|
const res = await saveUserInternal(dataReq);
|
||||||
const resData = res?.data?.data;
|
const resData = res?.data?.data;
|
||||||
const userProfileId = resData.id;
|
const userProfileId = resData?.id;
|
||||||
|
|
||||||
var placementArr: any[] = [];
|
var placementArr: any[] = [];
|
||||||
placementRows.forEach((row: any) => {
|
placementRows.forEach((row: any) => {
|
||||||
|
|
@ -131,15 +139,14 @@ export default function AddExpertForm() {
|
||||||
|
|
||||||
const dataReq2 = {
|
const dataReq2 = {
|
||||||
userId: userProfileId,
|
userId: userProfileId,
|
||||||
placements: placementArr
|
placements: placementArr,
|
||||||
}
|
};
|
||||||
const res2 = await saveUserRolePlacements(dataReq2);
|
const res2 = await saveUserRolePlacements(dataReq2);
|
||||||
const resData2 = res2?.data?.data;
|
const resData2 = res2?.data?.data;
|
||||||
|
|
||||||
success("/admin/add-experts");
|
success("/admin/add-experts");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function success(redirect: string): void {
|
function success(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>',
|
||||||
|
|
@ -180,7 +187,6 @@ export default function AddExpertForm() {
|
||||||
setUserLevels(levelsArr);
|
setUserLevels(levelsArr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function successSubmit() {
|
function successSubmit() {
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Sukses",
|
title: "Sukses",
|
||||||
|
|
@ -194,7 +200,11 @@ export default function AddExpertForm() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectionChange = (index: number, type: "roleId" | "userLevelId", value: string) => {
|
const handleSelectionChange = (
|
||||||
|
index: number,
|
||||||
|
type: "roleId" | "userLevelId",
|
||||||
|
value: string
|
||||||
|
) => {
|
||||||
setPlacementRows((prevRows) =>
|
setPlacementRows((prevRows) =>
|
||||||
prevRows.map((row) =>
|
prevRows.map((row) =>
|
||||||
row.index === index ? { ...row, [type]: value } : row
|
row.index === index ? { ...row, [type]: value } : row
|
||||||
|
|
@ -202,7 +212,6 @@ export default function AddExpertForm() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleRemoveRow = (index: number) => {
|
const handleRemoveRow = (index: number) => {
|
||||||
console.log(index);
|
console.log(index);
|
||||||
console.log(placementRows);
|
console.log(placementRows);
|
||||||
|
|
@ -212,7 +221,10 @@ export default function AddExpertForm() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddRow = () => {
|
const handleAddRow = () => {
|
||||||
setPlacementRows((prevRows: any) => [...prevRows, { index: incrementId, roleId: "", userLevelId: 0 }]);
|
setPlacementRows((prevRows: any) => [
|
||||||
|
...prevRows,
|
||||||
|
{ index: incrementId, roleId: "", userLevelId: 0 },
|
||||||
|
]);
|
||||||
setIncrementId((prevId) => prevId + 1);
|
setIncrementId((prevId) => prevId + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -372,10 +384,14 @@ export default function AddExpertForm() {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<FormLabel>Penempatan</FormLabel>
|
<FormLabel>Posisi</FormLabel>
|
||||||
{placementRows?.map((row: any) => (
|
{placementRows?.map((row: any) => (
|
||||||
<div key={row.index} className="flex items-center gap-2 my-2">
|
<div key={row.index} className="flex items-center gap-2 my-2">
|
||||||
<Select onValueChange={(e) => handleSelectionChange(row.index, "roleId", e)} >
|
<Select
|
||||||
|
onValueChange={(e) =>
|
||||||
|
handleSelectionChange(row.index, "roleId", e)
|
||||||
|
}
|
||||||
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Pilih Role" />
|
<SelectValue placeholder="Pilih Role" />
|
||||||
|
|
@ -389,7 +405,11 @@ export default function AddExpertForm() {
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<Select onValueChange={(e) => handleSelectionChange(row.index, "userLevelId", e)}>
|
<Select
|
||||||
|
onValueChange={(e) =>
|
||||||
|
handleSelectionChange(row.index, "userLevelId", e)
|
||||||
|
}
|
||||||
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Pilih User Level" />
|
<SelectValue placeholder="Pilih User Level" />
|
||||||
|
|
@ -415,11 +435,7 @@ export default function AddExpertForm() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<Button
|
<Button type="button" size="md" onClick={() => handleAddRow()}>
|
||||||
type="button"
|
|
||||||
size="md"
|
|
||||||
onClick={() => handleAddRow()}
|
|
||||||
>
|
|
||||||
Tambah
|
Tambah
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ export default function ContentManagement() {
|
||||||
const [ticket6, setTicket6] = useState("");
|
const [ticket6, setTicket6] = useState("");
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
const [isInternational, setIsInternational] = useState([false, false, false]);
|
||||||
|
|
||||||
const baseUrl = "https://db-mediahub.polri.go.id/";
|
const baseUrl = "https://analytic.sitani.info/";
|
||||||
const url = "https://db-mediahub.polri.go.id/trusted/";
|
const url = "https://analytic.sitani.info/trusted/";
|
||||||
|
|
||||||
const view1 =
|
const view1 =
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ export default function EmergencyIssue() {
|
||||||
const [ticket6, setTicket6] = useState("");
|
const [ticket6, setTicket6] = useState("");
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
const [isInternational, setIsInternational] = useState([false, false, false]);
|
||||||
|
|
||||||
const baseUrl = "https://db-mediahub.polri.go.id/";
|
const baseUrl = "https://analytic.sitani.info/";
|
||||||
const url = "https://db-mediahub.polri.go.id/trusted/";
|
const url = "https://analytic.sitani.info/trusted/";
|
||||||
|
|
||||||
const view1 =
|
const view1 =
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ export default function FeedbackCenter() {
|
||||||
const [ticket6, setTicket6] = useState("");
|
const [ticket6, setTicket6] = useState("");
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
const [isInternational, setIsInternational] = useState([false, false, false]);
|
||||||
|
|
||||||
const baseUrl = "https://db-mediahub.polri.go.id/";
|
const baseUrl = "https://analytic.sitani.info/";
|
||||||
const url = "https://db-mediahub.polri.go.id/trusted/";
|
const url = "https://analytic.sitani.info/trusted/";
|
||||||
|
|
||||||
const view1 =
|
const view1 =
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ export default function ContentManagement() {
|
||||||
const [ticket6, setTicket6] = useState("");
|
const [ticket6, setTicket6] = useState("");
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
const [isInternational, setIsInternational] = useState([false, false, false]);
|
||||||
|
|
||||||
const baseUrl = "https://db-mediahub.polri.go.id/";
|
const baseUrl = "https://analytic.sitani.info/";
|
||||||
const url = "https://db-mediahub.polri.go.id/trusted/";
|
const url = "https://analytic.sitani.info/trusted/";
|
||||||
|
|
||||||
const view1 =
|
const view1 =
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,33 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import UserExternalTable from "@/components/table/management-user/management-user-external-table";
|
import UserExternalTable from "@/components/table/management-user/management-user-external-table";
|
||||||
import UserInternalTable from "@/components/table/management-user/management-user-internal-table";
|
import UserInternalTable from "@/components/table/management-user/management-user-internal-table";
|
||||||
import InternalTable from "@/components/table/management-user/management-user-internal-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import DashboardVisualization from "@/components/visualization/dashboard-viz";
|
|
||||||
import ManagementUserVisualization from "@/components/visualization/management-user-viz";
|
import ManagementUserVisualization from "@/components/visualization/management-user-viz";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { PlusIcon } from "lucide-react";
|
import { PlusIcon } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
|
||||||
export default function ManagementUser() {
|
export default function ManagementUser() {
|
||||||
const [isInternal, setIsInternal] = useState(true);
|
const [isInternal, setIsInternal] = useState(true);
|
||||||
|
const [levelNumber, setLevelNumber] = useState<number | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const encryptedLevel = Cookies.get("ulne");
|
||||||
|
if (encryptedLevel) {
|
||||||
|
const decryptedLevel = getCookiesDecrypt("ulne");
|
||||||
|
setLevelNumber(Number(decryptedLevel));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
router.push("?page=1");
|
router.push("?page=1");
|
||||||
}, [isInternal]);
|
}, [isInternal]);
|
||||||
|
|
||||||
|
const showExternalButton = levelNumber !== 2 && levelNumber !== 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
|
|
@ -23,10 +36,7 @@ export default function ManagementUser() {
|
||||||
<ManagementUserVisualization />
|
<ManagementUserVisualization />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5">
|
||||||
id="table"
|
|
||||||
className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5"
|
|
||||||
>
|
|
||||||
<div className="flex justify-between py-3">
|
<div className="flex justify-between py-3">
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
Data User {isInternal ? "Internal" : "Eksternal"}
|
Data User {isInternal ? "Internal" : "Eksternal"}
|
||||||
|
|
@ -40,28 +50,31 @@ export default function ManagementUser() {
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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
|
||||||
rounded="md"
|
rounded="md"
|
||||||
onClick={() => setIsInternal(true)}
|
onClick={() => setIsInternal(true)}
|
||||||
className={` hover:text-white
|
className={`hover:text-white ${
|
||||||
${
|
!isInternal ? "bg-white text-black" : "bg-black text-white"
|
||||||
!isInternal ? "bg-white text-black " : "bg-black text-white "
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
User Internal
|
User Internal
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
rounded="md"
|
{showExternalButton && (
|
||||||
onClick={() => setIsInternal(false)}
|
<Button
|
||||||
className={`hover:text-white ${
|
rounded="md"
|
||||||
!isInternal ? "bg-black text-white " : "bg-white text-black "
|
onClick={() => setIsInternal(false)}
|
||||||
}
|
className={`hover:text-white ${
|
||||||
`}
|
!isInternal ? "bg-black text-white" : "bg-white text-black"
|
||||||
>
|
}`}
|
||||||
User Eksternal
|
>
|
||||||
</Button>
|
User Eksternal
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isInternal ? <UserInternalTable /> : <UserExternalTable />}
|
{isInternal ? <UserInternalTable /> : <UserExternalTable />}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,10 @@ export default function CreateCategoryModal() {
|
||||||
{t("add-category")}
|
{t("add-category")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent size="md">
|
<DialogContent
|
||||||
|
size="md"
|
||||||
|
className="sm:h-[300px] md:h-[300px] lg:h-[500px] overflow-y-auto"
|
||||||
|
>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle> {t("add-category")}</DialogTitle>
|
<DialogTitle> {t("add-category")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Tanggal",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("createdAt")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "account-type",
|
||||||
|
header: "Jenis Akun",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("account-type")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "userName",
|
||||||
|
header: "UserName",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("userName")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "accessMediahub",
|
||||||
|
header: "Akses Mediahub",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("accessMediahub")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "desaignWeb",
|
||||||
|
header: "Tampilan Desain Web",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("desaignWeb")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "navigation",
|
||||||
|
header: "Kemudahan Navigasi",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("navigation")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "fastAccess",
|
||||||
|
header: "Kecepatan Akses",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("fastAccess")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/admin/broadcast/campaign-list/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
UserIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./column";
|
||||||
|
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
getMediaBlastCampaignPage,
|
||||||
|
listDataMedia,
|
||||||
|
} from "@/service/broadcast/broadcast";
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { NewCampaignIcon } from "@/components/icon";
|
||||||
|
import search from "../../../app/chat/components/search";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Bar,
|
||||||
|
BarChart,
|
||||||
|
CartesianGrid,
|
||||||
|
Label,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
} from "recharts";
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
question: "Seberapa Sering User Mengakses\nMediaHub Polri",
|
||||||
|
total: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Tampilan dan Desain pada Website\nMediaHub Polri",
|
||||||
|
total: 90,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Kemudahan Navigasi pada Website\nMediaHub Polri",
|
||||||
|
total: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Kecepatan Akses pada Website\nMediaHub Polri",
|
||||||
|
total: 70,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question:
|
||||||
|
"Seberapa Akurat dan Terpercaya\nInformasi pada Website MediaHub Polri",
|
||||||
|
total: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question:
|
||||||
|
"Seberapa Lengkap Informasi dan Berita\npada Website MediaHub Polri",
|
||||||
|
total: 55,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Seberapa membantu dalam\nmendapatkan Informasi terkait Polri",
|
||||||
|
total: 52,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const SurveyListTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const [showData, setShowData] = React.useState("polri");
|
||||||
|
const [startDate, setStartDate] = React.useState("");
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const res = await getMediaBlastCampaignPage(page - 1);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * 10 + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", data);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value); // Perbarui state search
|
||||||
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex-1 text-xl font-medium text-default-900">Survey</div>
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-between">
|
||||||
|
<div className="w-full md:w-[200px] lg:w-[300px] px-2">
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
</InputGroupText>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search Judul..."
|
||||||
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<div className="mx-2 my-1">
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
value={startDate}
|
||||||
|
onChange={(e) => setStartDate(e.target.value)}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mx-3">
|
||||||
|
<Select>
|
||||||
|
<SelectTrigger className="w-[150px]">
|
||||||
|
<SelectValue placeholder="Select a filter" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>Filter</SelectLabel>
|
||||||
|
<SelectItem value="polri">Polri</SelectItem>
|
||||||
|
<SelectItem value="umum">Umum</SelectItem>
|
||||||
|
<SelectItem value="jurnalist">Journalist</SelectItem>
|
||||||
|
<SelectItem value="ksp">Ksp</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="min-h-screen p-3">
|
||||||
|
<h2 className="text-center font-semibold mb-4">
|
||||||
|
Survei Kepuasan Pengguna MediaHub Polri
|
||||||
|
</h2>
|
||||||
|
<ResponsiveContainer width="100%" height={500}>
|
||||||
|
<BarChart
|
||||||
|
layout="vertical"
|
||||||
|
data={data}
|
||||||
|
margin={{ top: 20, right: 30, left: 30, bottom: 80 }}
|
||||||
|
>
|
||||||
|
<CartesianGrid strokeDasharray="5 5" />
|
||||||
|
<XAxis type="number">
|
||||||
|
<Label
|
||||||
|
value="Total Survei"
|
||||||
|
offset={-30}
|
||||||
|
position="insideBottom"
|
||||||
|
style={{ textAnchor: "middle", fontStyle: "italic" }}
|
||||||
|
/>
|
||||||
|
</XAxis>
|
||||||
|
<YAxis
|
||||||
|
type="category"
|
||||||
|
dataKey="question"
|
||||||
|
width={240} // Lebarkan agar teks muat
|
||||||
|
tick={{
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
tickFormatter={(value: string) =>
|
||||||
|
value.length > 40 ? value.slice(0, 37) + "..." : value
|
||||||
|
} // Atur potong teks panjang, atau hapus kalau mau tampil semua
|
||||||
|
>
|
||||||
|
<Label
|
||||||
|
value="Pertanyaan"
|
||||||
|
angle={-90}
|
||||||
|
position="insideLeft"
|
||||||
|
style={{ textAnchor: "middle", fontStyle: "italic" }}
|
||||||
|
dx={-20}
|
||||||
|
/>
|
||||||
|
</YAxis>
|
||||||
|
<Tooltip />
|
||||||
|
<Bar dataKey="total" fill="#3163d4" />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SurveyListTable;
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import SurveyListTable from "./component/table";
|
||||||
|
|
||||||
|
export default function AdminSurvey() {
|
||||||
|
const [tab, setTab] = useState("Email Blast");
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<SurveyListTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -581,7 +581,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
{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 w-[250px]"
|
className="dark:bg-background dark:text-foreground w-full"
|
||||||
>
|
>
|
||||||
<Plus className="w-4 h-4 me-1" />
|
<Plus className="w-4 h-4 me-1" />
|
||||||
{t("addEvent")}
|
{t("addEvent")}
|
||||||
|
|
@ -593,8 +593,8 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
{roleId === 3 && userLevelId === 216 ? (
|
{roleId === 3 && userLevelId === 216 ? (
|
||||||
<Button className="dark:bg-background dark:text-foreground w-[250px]">
|
<Button className="dark:bg-background dark:text-foreground w-full">
|
||||||
<Book className="w-4 h-4" />
|
<Book size={15} className="w-4 h-4 mr-3" />
|
||||||
{t("bag-pa-monitoring-results")}
|
{t("bag-pa-monitoring-results")}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -775,7 +775,7 @@ const EventModal = ({
|
||||||
checked={wilayahPublish.semua}
|
checked={wilayahPublish.semua}
|
||||||
onCheckedChange={() => toggleWilayah("semua")}
|
onCheckedChange={() => toggleWilayah("semua")}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="semua" className="ml-2">
|
<label htmlFor="semua" className="ml-2 text-sm">
|
||||||
Semua
|
Semua
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -785,7 +785,7 @@ const EventModal = ({
|
||||||
checked={wilayahPublish.nasional}
|
checked={wilayahPublish.nasional}
|
||||||
onCheckedChange={() => toggleWilayah("nasional")}
|
onCheckedChange={() => toggleWilayah("nasional")}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="nasional" className="ml-2">
|
<label htmlFor="nasional" className="ml-2 text-sm mr-2">
|
||||||
Nasional
|
Nasional
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -795,7 +795,7 @@ const EventModal = ({
|
||||||
checked={wilayahPublish.polda}
|
checked={wilayahPublish.polda}
|
||||||
onCheckedChange={() => toggleWilayah("polda")}
|
onCheckedChange={() => toggleWilayah("polda")}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="polda" className="mx-2">
|
<label htmlFor="polda" className="mx-2 text-sm mr-2">
|
||||||
Polda
|
Polda
|
||||||
</label>
|
</label>
|
||||||
{wilayahPublish.polda && (
|
{wilayahPublish.polda && (
|
||||||
|
|
@ -814,7 +814,7 @@ const EventModal = ({
|
||||||
checked={wilayahPublish.polres}
|
checked={wilayahPublish.polres}
|
||||||
onCheckedChange={() => toggleWilayah("polres")}
|
onCheckedChange={() => toggleWilayah("polres")}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="polres" className="ml-2">
|
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
||||||
Polres
|
Polres
|
||||||
</label>
|
</label>
|
||||||
{wilayahPublish.polres && (
|
{wilayahPublish.polres && (
|
||||||
|
|
@ -833,7 +833,7 @@ const EventModal = ({
|
||||||
checked={wilayahPublish.satker}
|
checked={wilayahPublish.satker}
|
||||||
onCheckedChange={() => toggleWilayah("satker")}
|
onCheckedChange={() => toggleWilayah("satker")}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="satker" className="mx-2">
|
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||||
Satker
|
Satker
|
||||||
</label>
|
</label>
|
||||||
{wilayahPublish.satker && (
|
{wilayahPublish.satker && (
|
||||||
|
|
@ -852,7 +852,10 @@ const EventModal = ({
|
||||||
checked={wilayahPublish.international}
|
checked={wilayahPublish.international}
|
||||||
onCheckedChange={() => toggleWilayah("international")}
|
onCheckedChange={() => toggleWilayah("international")}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="international" className="ml-2">
|
<label
|
||||||
|
htmlFor="international"
|
||||||
|
className="ml-2 text-sm mr-2"
|
||||||
|
>
|
||||||
Internasional
|
Internasional
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
@ -60,9 +62,10 @@ const BlogTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
|
@ -106,12 +109,12 @@ const BlogTable = () => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
}, [categoryFilter, statusFilter, page, limit, search]);
|
}, [categoryFilter, statusFilter, page, showData, search]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await paginationBlog(
|
const res = await paginationBlog(
|
||||||
limit,
|
showData,
|
||||||
page - 1,
|
page - 1,
|
||||||
search,
|
search,
|
||||||
categoryFilter,
|
categoryFilter,
|
||||||
|
|
@ -120,7 +123,7 @@ const BlogTable = () => {
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
@ -183,7 +186,7 @@ const BlogTable = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/blog/create"}>
|
<Link href={"/contributor/blog/create"}>
|
||||||
<Button fullWidth color="primary">
|
<Button fullWidth color="primary">
|
||||||
<Plus className="w-6 h-6 me-1.5" />
|
<Plus size={18} className=" me-1.5" />
|
||||||
{t("create-indeks")}
|
{t("create-indeks")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -210,6 +213,34 @@ const BlogTable = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ import {
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -76,9 +78,10 @@ const TableAudio = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
|
@ -132,7 +135,15 @@ const TableAudio = () => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
}, [categoryFilter, statusFilter, page, limit, search, startDate, endDate]);
|
}, [
|
||||||
|
categoryFilter,
|
||||||
|
statusFilter,
|
||||||
|
page,
|
||||||
|
showData,
|
||||||
|
search,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const category = await listEnableCategory("4");
|
const category = await listEnableCategory("4");
|
||||||
|
|
@ -171,7 +182,7 @@ const TableAudio = () => {
|
||||||
try {
|
try {
|
||||||
const isForSelf = Number(roleId) === 4;
|
const isForSelf = Number(roleId) === 4;
|
||||||
const res = await listDataAudio(
|
const res = await listDataAudio(
|
||||||
limit,
|
showData,
|
||||||
page - 1,
|
page - 1,
|
||||||
isForSelf,
|
isForSelf,
|
||||||
!isForSelf,
|
!isForSelf,
|
||||||
|
|
@ -189,7 +200,7 @@ const TableAudio = () => {
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
|
|
@ -248,6 +259,34 @@ const TableAudio = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
{showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ const ReactTableAudioPage = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/content/audio/create"}>
|
<Link href={"/contributor/content/audio/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-audio")}
|
{t("create-audio")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ import {
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -81,13 +83,13 @@ const TableImage = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
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 [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");
|
||||||
|
|
@ -96,6 +98,7 @@ const TableImage = () => {
|
||||||
const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
|
const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
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 [startDate, setStartDate] = React.useState("");
|
const [startDate, setStartDate] = React.useState("");
|
||||||
|
|
@ -135,10 +138,17 @@ const TableImage = () => {
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// Panggil fetchData saat filter kategori berubah
|
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
}, [categoryFilter, statusFilter, page, limit, search, startDate, endDate]);
|
}, [
|
||||||
|
categoryFilter,
|
||||||
|
statusFilter,
|
||||||
|
page,
|
||||||
|
showData,
|
||||||
|
search,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const category = await listEnableCategory("1");
|
const category = await listEnableCategory("1");
|
||||||
|
|
@ -177,7 +187,7 @@ const TableImage = () => {
|
||||||
try {
|
try {
|
||||||
const isForSelf = Number(roleId) === 4;
|
const isForSelf = Number(roleId) === 4;
|
||||||
const res = await listDataImage(
|
const res = await listDataImage(
|
||||||
limit,
|
showData,
|
||||||
page - 1,
|
page - 1,
|
||||||
isForSelf,
|
isForSelf,
|
||||||
!isForSelf,
|
!isForSelf,
|
||||||
|
|
@ -195,7 +205,7 @@ const TableImage = () => {
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
|
|
@ -254,6 +264,34 @@ const TableImage = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
{showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ const ReactTableImagePage = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/content/image/create"}>
|
<Link href={"/contributor/content/image/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-image")}
|
{t("create-image")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ import {
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -75,9 +77,10 @@ const TableTeks = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
|
@ -131,7 +134,15 @@ const TableTeks = () => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
}, [categoryFilter, statusFilter, page, limit, search, startDate, endDate]);
|
}, [
|
||||||
|
categoryFilter,
|
||||||
|
statusFilter,
|
||||||
|
page,
|
||||||
|
showData,
|
||||||
|
search,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const category = await listEnableCategory("3");
|
const category = await listEnableCategory("3");
|
||||||
|
|
@ -170,7 +181,7 @@ const TableTeks = () => {
|
||||||
try {
|
try {
|
||||||
const isForSelf = Number(roleId) === 4;
|
const isForSelf = Number(roleId) === 4;
|
||||||
const res = await listDataTeks(
|
const res = await listDataTeks(
|
||||||
limit,
|
showData,
|
||||||
page - 1,
|
page - 1,
|
||||||
isForSelf,
|
isForSelf,
|
||||||
!isForSelf,
|
!isForSelf,
|
||||||
|
|
@ -188,7 +199,7 @@ const TableTeks = () => {
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
|
|
@ -247,6 +258,34 @@ const TableTeks = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
{showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ const ReactTableTeksPage = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/content/teks/create"}>
|
<Link href={"/contributor/content/teks/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-text")}
|
{t("create-text")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ import {
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -75,9 +77,10 @@ const TableVideo = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
|
@ -131,7 +134,16 @@ const TableVideo = () => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
}, [categoryFilter, statusFilter, page, limit, search, startDate, endDate]);
|
}, [
|
||||||
|
categoryFilter,
|
||||||
|
statusFilter,
|
||||||
|
page,
|
||||||
|
showData,
|
||||||
|
,
|
||||||
|
search,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const category = await listEnableCategory("2");
|
const category = await listEnableCategory("2");
|
||||||
|
|
@ -170,7 +182,7 @@ const TableVideo = () => {
|
||||||
try {
|
try {
|
||||||
const isForSelf = Number(roleId) === 4;
|
const isForSelf = Number(roleId) === 4;
|
||||||
const res = await listDataVideo(
|
const res = await listDataVideo(
|
||||||
limit,
|
showData,
|
||||||
page - 1,
|
page - 1,
|
||||||
isForSelf,
|
isForSelf,
|
||||||
!isForSelf,
|
!isForSelf,
|
||||||
|
|
@ -188,7 +200,7 @@ const TableVideo = () => {
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
|
|
@ -248,6 +260,34 @@ const TableVideo = () => {
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
{showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ const ReactTableVideoPage = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/content/video/create"}>
|
<Link href={"/contributor/content/video/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-video")}
|
{t("create-video")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
Upload,
|
||||||
|
UploadCloud,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link, useRouter } from "@/components/navigation";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { deleteBlog } from "@/service/blog/blog";
|
||||||
|
import { error, loading } from "@/lib/swal";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const useTableColumns = ({
|
||||||
|
handlePreview,
|
||||||
|
}: {
|
||||||
|
handlePreview: (id: string) => void;
|
||||||
|
}) => {
|
||||||
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: t("no"),
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: t("title"),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="whitespace-normal">{row.getValue("title")}</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: "version",
|
||||||
|
header: t("version"),
|
||||||
|
cell: ({ row }) => <span className="">{row.getValue("version")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
function success() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteBlog = (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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownload = async (id: string) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`https://netidhub.com/api/media/report/download?id=${id}`,
|
||||||
|
{
|
||||||
|
responseType: "blob",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute("download", `report-${id}.pdf`);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Download failed", error);
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Gagal",
|
||||||
|
text: "Terjadi kesalahan saat mengunduh file.",
|
||||||
|
icon: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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={() => handlePreview(row.original.id)}
|
||||||
|
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" />
|
||||||
|
Preview
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleDownload(row.original.id)}
|
||||||
|
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"
|
||||||
|
>
|
||||||
|
<Upload className="w-4 h-4 me-1.5" />
|
||||||
|
Download
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTableColumns;
|
||||||
|
|
@ -0,0 +1,479 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import { ChevronDown, Plus, Search } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { getBlogCategory, paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./columns";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import useTableColumns from "./columns";
|
||||||
|
import {
|
||||||
|
getPreviewById,
|
||||||
|
paginationReport,
|
||||||
|
saveReport,
|
||||||
|
} from "@/service/report/report";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
|
||||||
|
type PreviewApiResponse = {
|
||||||
|
error: boolean;
|
||||||
|
message: string;
|
||||||
|
data: {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
filePath: string;
|
||||||
|
version: number;
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReportTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const t = useTranslations("Report");
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const [categories, setCategories] = React.useState<any[]>([]);
|
||||||
|
const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [categoryFilter, setCategoryFilter] = React.useState<string>("");
|
||||||
|
const [dateFilter, setDateFilter] = React.useState("");
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
||||||
|
const [openPreview, setOpenPreview] = React.useState(false);
|
||||||
|
const [previewData, setPreviewData] = React.useState<any>(null);
|
||||||
|
|
||||||
|
const handlePreview = (id: string) => {
|
||||||
|
const url = `https://netidhub.com/api/media/report/view?id=${id}`;
|
||||||
|
setPreviewData({ url });
|
||||||
|
setOpenPreview(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = useTableColumns({ handlePreview });
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
getCategories();
|
||||||
|
}, [categoryFilter, statusFilter, page, showData, search]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await paginationReport(
|
||||||
|
showData,
|
||||||
|
page - 1,
|
||||||
|
search,
|
||||||
|
categoryFilter,
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCategories() {
|
||||||
|
const category = await getBlogCategory();
|
||||||
|
const resCategory = category?.data?.data?.content;
|
||||||
|
setCategories(resCategory || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCheckboxChange = (categoryId: number) => {
|
||||||
|
setSelectedCategories((prev: any) =>
|
||||||
|
prev.includes(categoryId)
|
||||||
|
? prev.filter((id: any) => id !== categoryId)
|
||||||
|
: [...prev, categoryId]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Perbarui filter kategori
|
||||||
|
setCategoryFilter((prev) => {
|
||||||
|
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
||||||
|
|
||||||
|
const newCategories = updatedCategories.includes(categoryId)
|
||||||
|
? updatedCategories.filter((id) => id !== categoryId)
|
||||||
|
: [...updatedCategories, categoryId];
|
||||||
|
|
||||||
|
return newCategories.join(",");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleStatusCheckboxChange(value: any) {
|
||||||
|
setStatusFilter((prev: any) =>
|
||||||
|
prev.includes(value)
|
||||||
|
? prev.filter((status: any) => status !== value)
|
||||||
|
: [...prev, value]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value); // Perbarui state search
|
||||||
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerateReport = async () => {
|
||||||
|
const today = new Date();
|
||||||
|
const formattedDate = format(today, "dd-MM-yyyy"); // Hasil: 22-04-2025
|
||||||
|
const title = `Report ${formattedDate}`;
|
||||||
|
|
||||||
|
const requestData = {
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await saveReport(requestData);
|
||||||
|
|
||||||
|
if (response?.error) {
|
||||||
|
MySwal.fire(
|
||||||
|
"Error",
|
||||||
|
response?.message || "Gagal menyimpan laporan",
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
text: "Laporan berhasil dibuat.",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then(() => {
|
||||||
|
fetchData(); // Refresh tabel setelah generate
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Generate report error:", error);
|
||||||
|
MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog open={openPreview} onOpenChange={setOpenPreview}>
|
||||||
|
<DialogContent className="min-w-max h-[500px] p-0 overflow-hidden">
|
||||||
|
<DialogHeader className="p-4 border-b">
|
||||||
|
<DialogTitle>Preview Laporan</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="h-full w-[1000px] overflow-auto">
|
||||||
|
{previewData ? (
|
||||||
|
<iframe
|
||||||
|
src={previewData.url}
|
||||||
|
className="w-full h-[calc(100vh-100px)]"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="p-4">Loading preview...</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
|
{t("table")} {t("report")}
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Button fullWidth color="primary" onClick={handleGenerateReport}>
|
||||||
|
<Plus size={18} className=" me-1.5" />
|
||||||
|
{t("generate-report")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex flex-col md:flex-row lg:flex-row md:justify-between lg:justify-between items-center md:px-5 lg:px-5">
|
||||||
|
<div className="w-full md:w-[200px] lg:w-[200px] px-2">
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
</InputGroupText>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search Title"
|
||||||
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center gap-3">
|
||||||
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="ml-auto w-full sm:w-[100px]"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
Filter <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-64 h-[200px] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
|
<p>Filter</p>
|
||||||
|
</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>
|
||||||
|
{/* <div className="mx-2 my-1">
|
||||||
|
<Label>Code</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={filterByCode}
|
||||||
|
// onChange={handleSearchFilterByCode}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-1"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-1" className="text-sm">
|
||||||
|
{t("wait-review")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-2"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-2" className="text-sm">
|
||||||
|
{t("acc")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center py-4">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
Columns <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
{table
|
||||||
|
.getAllColumns()
|
||||||
|
.filter((column) => column.getCanHide())
|
||||||
|
.map((column) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
key={column.id}
|
||||||
|
className="capitalize"
|
||||||
|
checked={column.getIsVisible()}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
column.toggleVisibility(!!value)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{column.id}
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportTable;
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
export const metadata = {
|
||||||
|
title: "Blog",
|
||||||
|
};
|
||||||
|
|
||||||
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import BlogTable from "./components/report-table";
|
||||||
|
import { Plus } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
import ReportTable from "./components/report-table";
|
||||||
|
|
||||||
|
const ReportPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-0">
|
||||||
|
<ReportTable />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportPage;
|
||||||
|
|
@ -22,7 +22,7 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Search, UploadIcon } from "lucide-react";
|
import { ChevronDown, Search, UploadIcon } from "lucide-react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
|
@ -34,6 +34,14 @@ 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";
|
import useTableColumns from "./columns";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
const EventTable = () => {
|
const EventTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -48,14 +56,16 @@ const EventTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
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 [limit, setLimit] = React.useState(10);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const columns = useTableColumns();
|
const columns = useTableColumns();
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -87,15 +97,21 @@ const EventTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit, search]);
|
}, [page, showData, search, statusFilter]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await paginationSchedule(limit, page - 1, 2, search);
|
const res = await paginationSchedule(
|
||||||
|
showData,
|
||||||
|
page - 1,
|
||||||
|
2,
|
||||||
|
search,
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
@ -108,6 +124,14 @@ const EventTable = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleStatusCheckboxChange = (statusId: number) => {
|
||||||
|
const updatedFilter = statusFilter.includes(statusId)
|
||||||
|
? statusFilter.filter((id) => id !== statusId)
|
||||||
|
: [...statusFilter, statusId];
|
||||||
|
|
||||||
|
setStatusFilter(updatedFilter);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value); // Perbarui state search
|
setSearch(e.target.value); // Perbarui state search
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
|
@ -124,7 +148,7 @@ const EventTable = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/schedule/event/create"}>
|
<Link href={"/contributor/schedule/event/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-schedule")}
|
{t("create-schedule")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -133,8 +157,8 @@ const EventTable = () => {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center px-5 gap-2">
|
<div className="flex flex-col sm:flex-row md:flex-row lg:flex-row justify-between items-center px-3 gap-y-2">
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="w-full sm:w-[150px] md:w-[250px] lg:w-[250px]">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -148,17 +172,78 @@ const EventTable = () => {
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="flex flex-row">
|
||||||
<Input
|
<div className="mx-3">
|
||||||
placeholder="Filter Status..."
|
<DropdownMenu>
|
||||||
value={
|
<DropdownMenuTrigger asChild>
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
<Button size="md" variant="outline">
|
||||||
}
|
1 - {showData} Data
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
</Button>
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
</DropdownMenuTrigger>
|
||||||
}
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
className="max-w-sm "
|
<DropdownMenuRadioGroup
|
||||||
/>
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
Filter <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-64 h-[150px] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
|
<p>Filter</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-1"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-1" className="text-sm">
|
||||||
|
Menunggu Review
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-2"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-2" className="text-sm">
|
||||||
|
Diterima
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
|
ChevronDown,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Eye,
|
Eye,
|
||||||
|
|
@ -47,6 +48,14 @@ 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";
|
import useTableColumns from "./columns";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
const PressConferenceTable = () => {
|
const PressConferenceTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -61,14 +70,16 @@ const PressConferenceTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
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 [limit, setLimit] = React.useState(10);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const columns = useTableColumns();
|
const columns = useTableColumns();
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -100,15 +111,21 @@ const PressConferenceTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit, search]);
|
}, [page, showData, search, statusFilter]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await paginationSchedule(limit, page - 1, 1, search);
|
const res = await paginationSchedule(
|
||||||
|
showData,
|
||||||
|
page - 1,
|
||||||
|
1,
|
||||||
|
search,
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
@ -121,6 +138,14 @@ const PressConferenceTable = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleStatusCheckboxChange = (statusId: number) => {
|
||||||
|
const updatedFilter = statusFilter.includes(statusId)
|
||||||
|
? statusFilter.filter((id) => id !== statusId)
|
||||||
|
: [...statusFilter, statusId];
|
||||||
|
|
||||||
|
setStatusFilter(updatedFilter);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value); // Perbarui state search
|
setSearch(e.target.value); // Perbarui state search
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
|
@ -137,7 +162,7 @@ const PressConferenceTable = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/schedule/press-conference/create"}>
|
<Link href={"/contributor/schedule/press-conference/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-schedule")}
|
{t("create-schedule")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -146,8 +171,8 @@ const PressConferenceTable = () => {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center px-5 gap-2">
|
<div className="flex flex-col sm:flex-row md:flex-row lg:flex-row justify-between items-center px-3 gap-y-2">
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="w-full sm:w-[150px] md:w-[250px] lg:w-[250px]">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -161,17 +186,78 @@ const PressConferenceTable = () => {
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="flex flex-row gap-3">
|
||||||
<Input
|
<div className="mx-3">
|
||||||
placeholder="Filter Status..."
|
<DropdownMenu>
|
||||||
value={
|
<DropdownMenuTrigger asChild>
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
<Button size="md" variant="outline">
|
||||||
}
|
1 - {showData} Data
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
</Button>
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
</DropdownMenuTrigger>
|
||||||
}
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
className="max-w-sm "
|
<DropdownMenuRadioGroup
|
||||||
/>
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
Filter <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-64 h-[150px] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
|
<p>Filter</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-1"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-1" className="text-sm">
|
||||||
|
Menunggu Review
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-2"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-2" className="text-sm">
|
||||||
|
Diterima
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
|
ChevronDown,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Eye,
|
Eye,
|
||||||
|
|
@ -48,6 +49,14 @@ 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";
|
import useTableColumns from "./columns";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
const PressReleaseTable = () => {
|
const PressReleaseTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -62,14 +71,16 @@ const PressReleaseTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
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 [limit, setLimit] = React.useState(10);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const columns = useTableColumns();
|
const columns = useTableColumns();
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -101,15 +112,21 @@ const PressReleaseTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit, search]);
|
}, [page, showData, search, statusFilter]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await paginationSchedule(limit, page - 1, 3, search);
|
const res = await paginationSchedule(
|
||||||
|
showData,
|
||||||
|
page - 1,
|
||||||
|
3,
|
||||||
|
search,
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
@ -122,6 +139,14 @@ const PressReleaseTable = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleStatusCheckboxChange = (statusId: number) => {
|
||||||
|
const updatedFilter = statusFilter.includes(statusId)
|
||||||
|
? statusFilter.filter((id) => id !== statusId)
|
||||||
|
: [...statusFilter, statusId];
|
||||||
|
|
||||||
|
setStatusFilter(updatedFilter);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value); // Perbarui state search
|
setSearch(e.target.value); // Perbarui state search
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
|
@ -138,7 +163,7 @@ const PressReleaseTable = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/schedule/press-release/create"}>
|
<Link href={"/contributor/schedule/press-release/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-schedule")}
|
{t("create-schedule")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -147,8 +172,8 @@ const PressReleaseTable = () => {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center px-5 gap-2">
|
<div className="flex flex-col sm:flex-row md:flex-row lg:flex-row justify-between items-center px-3 gap-y-2">
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="w-full sm:w-[150px] md:w-[250px] lg:w-[250px]">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -162,17 +187,78 @@ const PressReleaseTable = () => {
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="flex flex-row">
|
||||||
<Input
|
<div className="mx-3">
|
||||||
placeholder="Filter Status..."
|
<DropdownMenu>
|
||||||
value={
|
<DropdownMenuTrigger asChild>
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
<Button size="md" variant="outline">
|
||||||
}
|
1 - {showData} Data
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
</Button>
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
</DropdownMenuTrigger>
|
||||||
}
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
className="w-full "
|
<DropdownMenuRadioGroup
|
||||||
/>
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
Filter <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-64 h-[150px] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
|
<p>Filter</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-1"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-1" className="text-sm">
|
||||||
|
Menunggu Review
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-2"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-2" className="text-sm">
|
||||||
|
Diterima
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
|
|
@ -228,7 +314,6 @@ const PressReleaseTable = () => {
|
||||||
totalPage={totalPage}
|
totalPage={totalPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
\{" "}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { deleteCategory } from "@/service/settings/settings";
|
||||||
|
import { deleteTask } from "@/service/task";
|
||||||
|
import { error, loading } from "@/lib/swal";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
const useTableColumns = () => {
|
||||||
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: t("no"),
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: t("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: "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-ta/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-ta/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;
|
||||||
|
|
@ -0,0 +1,405 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronDown,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./columns";
|
||||||
|
import { listTask, listTaskTa } from "@/service/task";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import useTableColumns from "./columns";
|
||||||
|
|
||||||
|
const TaskTaTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const t = useTranslations("AnalyticsDashboard");
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
|
const [dateFilter, setDateFilter] = React.useState("");
|
||||||
|
const [endDate, setEndDate] = React.useState("");
|
||||||
|
const [filterByCode, setFilterByCode] = React.useState<string>("");
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
const [isSpecificAttention, setIsSpecificAttention] = React.useState(true);
|
||||||
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const columns = useTableColumns();
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [
|
||||||
|
page,
|
||||||
|
showData,
|
||||||
|
isSpecificAttention,
|
||||||
|
search,
|
||||||
|
dateFilter,
|
||||||
|
filterByCode,
|
||||||
|
statusFilter,
|
||||||
|
]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
const formattedStartDate = dateFilter
|
||||||
|
? format(new Date(dateFilter), "yyyy-MM-dd")
|
||||||
|
: "";
|
||||||
|
try {
|
||||||
|
const res = await listTaskTa(
|
||||||
|
page - 1,
|
||||||
|
search,
|
||||||
|
showData,
|
||||||
|
filterByCode,
|
||||||
|
formattedStartDate,
|
||||||
|
isSpecificAttention ? "atensi-khusus" : "tugas-harian",
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
|
||||||
|
// let contentDataFilter = res?.data?.data?.content || [];
|
||||||
|
|
||||||
|
// Filter berdasarkan status
|
||||||
|
// contentDataFilter = contentDataFilter.filter((item: any) => {
|
||||||
|
// const isSelesai = statusFilter.includes(1) ? item.isDone : true;
|
||||||
|
// const isAktif = statusFilter.includes(2) ? item.isActive : true;
|
||||||
|
// return isSelesai && isAktif;
|
||||||
|
// });
|
||||||
|
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setFilterByCode(e.target.value);
|
||||||
|
setSearch(e.target.value);
|
||||||
|
table.getColumn("judul")?.setFilterValue(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleStatusCheckboxChange(value: number) {
|
||||||
|
setStatusFilter((prev) =>
|
||||||
|
prev.includes(value)
|
||||||
|
? prev.filter((status) => status !== value)
|
||||||
|
: [...prev, value]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const handleSearchFilterByCode = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// const value = e.target.value;
|
||||||
|
// console.log("code :", value);
|
||||||
|
// setFilterByCode(value);
|
||||||
|
// fetchData();
|
||||||
|
// };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="mx-5 mb-3">
|
||||||
|
<div className="">
|
||||||
|
<div className="row">
|
||||||
|
<div className="flex justify-between mb-6">
|
||||||
|
<label className="inline-flex text-md cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
onChange={() => setIsSpecificAttention(!isSpecificAttention)}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={` ${
|
||||||
|
isSpecificAttention
|
||||||
|
? "bg-default-900 text-white"
|
||||||
|
: "dark:text-default-700 border-2"
|
||||||
|
}
|
||||||
|
px-[18px] py-1 transition duration-100 rounded`}
|
||||||
|
>
|
||||||
|
{t("special-attention")}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
${
|
||||||
|
!isSpecificAttention
|
||||||
|
? "bg-default-900 text-white"
|
||||||
|
: " dark:text-default-700 border-2"
|
||||||
|
}
|
||||||
|
px-[18px] py-1 transition duration-100 rounded
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{t("daily-tasks")}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row lg:flex-row justify-between sm:items-center md:items-center lg:items-center px-5">
|
||||||
|
<div className="mb-3 sm:mb-0 lg-mb-0">
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
</InputGroupText>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search Title dan Code"
|
||||||
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white w-full"
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className=" flex flex-row items-center gap-3">
|
||||||
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="ml-auto w-full sm:w-[100px]"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
Filter <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-64 h-[200px] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
|
<p>Filter</p>
|
||||||
|
</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>
|
||||||
|
{/* <div className="mx-2 my-1">
|
||||||
|
<Label>Code</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={filterByCode}
|
||||||
|
// onChange={handleSearchFilterByCode}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-1"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-1" className="text-sm">
|
||||||
|
{t("done")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-2"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-2" className="text-sm">
|
||||||
|
{t("active")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* <div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskTaTable;
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTaskTa from "@/components/form/task-ta/task-ta-form";
|
||||||
|
|
||||||
|
const TaskTaCreatePage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormTaskTa />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskTaCreatePage;
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTask from "@/components/form/task/task-form";
|
||||||
|
import FormTaskDetail from "@/components/form/task/task-detail-form";
|
||||||
|
import FormTaskTaDetail from "@/components/form/task-ta/task-ta-detail-form";
|
||||||
|
|
||||||
|
const TaskTaDetailPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormTaskTaDetail />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskTaDetailPage;
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
export const metadata = {
|
||||||
|
title: "Task",
|
||||||
|
};
|
||||||
|
|
||||||
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
"use client";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import TaskTable from "./components/task-ta-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { UploadIcon } from "lucide-react";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
import { checkAuthorization, checkLoginSession } from "@/lib/utils";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import TaskTaTable from "./components/task-ta-table";
|
||||||
|
|
||||||
|
const TaskTaPage = () => {
|
||||||
|
const t = useTranslations("AnalyticsDashboard");
|
||||||
|
useEffect(() => {
|
||||||
|
function initState() {
|
||||||
|
checkAuthorization("admin"); // Specify the page, e.g., "admin" or another value
|
||||||
|
checkLoginSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex flex-col sm:flex-row lg:flex-row lg:items-center">
|
||||||
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
|
{t("tabel")} {t("task-ta")}
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Link href={"/contributor/task-ta/create"}>
|
||||||
|
<Button color="primary" className="text-white">
|
||||||
|
<UploadIcon size={18} className="mr-2" />
|
||||||
|
{t("create-task")}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="p-0">
|
||||||
|
<TaskTaTable />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskTaPage;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTask from "@/components/form/task/task-form";
|
||||||
|
import FormTaskDetail from "@/components/form/task/task-detail-form";
|
||||||
|
import FormTaskEdit from "@/components/form/task/task-edit-form";
|
||||||
|
import FormTaskTaEdit from "@/components/form/task-ta/task-ta-edit-form";
|
||||||
|
|
||||||
|
const TaskTaDetailPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormTaskTaEdit />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskTaDetailPage;
|
||||||
|
|
@ -42,6 +42,8 @@ import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -71,9 +73,10 @@ const TaskTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const [dateFilter, setDateFilter] = React.useState("");
|
const [dateFilter, setDateFilter] = React.useState("");
|
||||||
|
|
@ -117,7 +120,7 @@ const TaskTable = () => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [
|
}, [
|
||||||
page,
|
page,
|
||||||
limit,
|
showData,
|
||||||
isSpecificAttention,
|
isSpecificAttention,
|
||||||
search,
|
search,
|
||||||
dateFilter,
|
dateFilter,
|
||||||
|
|
@ -133,7 +136,7 @@ const TaskTable = () => {
|
||||||
const res = await listTask(
|
const res = await listTask(
|
||||||
page - 1,
|
page - 1,
|
||||||
search,
|
search,
|
||||||
limit,
|
showData,
|
||||||
filterByCode,
|
filterByCode,
|
||||||
formattedStartDate,
|
formattedStartDate,
|
||||||
isSpecificAttention ? "atensi-khusus" : "tugas-harian",
|
isSpecificAttention ? "atensi-khusus" : "tugas-harian",
|
||||||
|
|
@ -153,7 +156,7 @@ const TaskTable = () => {
|
||||||
// });
|
// });
|
||||||
|
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
@ -241,36 +244,64 @@ const TaskTable = () => {
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full sm:w-[100px] items-center gap-2">
|
|
||||||
<div className=" gap-3">
|
<div className=" flex flex-row items-center gap-3">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
|
<div className="mx-3">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button size="md" variant="outline">
|
||||||
variant="outline"
|
1 - {showData} Data
|
||||||
className="ml-auto w-full sm:w-[100px]"
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
Filter <ChevronDown />
|
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
align="end"
|
<DropdownMenuRadioGroup
|
||||||
className="w-64 h-[200px] overflow-y-auto"
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="ml-auto w-full sm:w-[100px]"
|
||||||
|
size="md"
|
||||||
>
|
>
|
||||||
<div className="flex flex-row justify-between my-1 mx-1">
|
Filter <ChevronDown />
|
||||||
<p>Filter</p>
|
</Button>
|
||||||
</div>
|
</DropdownMenuTrigger>
|
||||||
<div className="mx-2 my-1">
|
<DropdownMenuContent
|
||||||
<Label>{t("date")}</Label>
|
align="end"
|
||||||
<Input
|
className="w-64 h-[200px] overflow-y-auto"
|
||||||
type="date"
|
>
|
||||||
value={dateFilter}
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
onChange={(e) => setDateFilter(e.target.value)}
|
<p>Filter</p>
|
||||||
className="max-w-sm"
|
</div>
|
||||||
/>
|
<div className="mx-2 my-1">
|
||||||
</div>
|
<Label>{t("date")}</Label>
|
||||||
{/* <div className="mx-2 my-1">
|
<Input
|
||||||
|
type="date"
|
||||||
|
value={dateFilter}
|
||||||
|
onChange={(e) => setDateFilter(e.target.value)}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* <div className="mx-2 my-1">
|
||||||
<Label>Code</Label>
|
<Label>Code</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter Status..."
|
placeholder="Filter Status..."
|
||||||
|
|
@ -279,36 +310,36 @@ const TaskTable = () => {
|
||||||
className="max-w-sm"
|
className="max-w-sm"
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</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
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="status-1"
|
id="status-1"
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
checked={statusFilter.includes(1)}
|
checked={statusFilter.includes(1)}
|
||||||
onChange={() => handleStatusCheckboxChange(1)}
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="status-1" className="text-sm">
|
<label htmlFor="status-1" className="text-sm">
|
||||||
{t("done")}
|
{t("done")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center px-4 py-1">
|
<div className="flex items-center px-4 py-1">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="status-2"
|
id="status-2"
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
checked={statusFilter.includes(2)}
|
checked={statusFilter.includes(2)}
|
||||||
onChange={() => handleStatusCheckboxChange(2)}
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="status-2" className="text-sm">
|
<label htmlFor="status-2" className="text-sm">
|
||||||
{t("active")}
|
{t("active")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="flex-none">
|
</div>
|
||||||
|
{/* <div className="flex-none">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter Status..."
|
placeholder="Filter Status..."
|
||||||
value={
|
value={
|
||||||
|
|
@ -320,7 +351,6 @@ const TaskTable = () => {
|
||||||
className="max-w-sm "
|
className="max-w-sm "
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div> */}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ const TaskPage = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/contributor/task/create"}>
|
<Link href={"/contributor/task/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-task")}
|
{t("create-task")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -168,8 +168,8 @@ export default function ExecutiveDashboard() {
|
||||||
const [ticket6, setTicket6] = useState("");
|
const [ticket6, setTicket6] = useState("");
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
const [isInternational, setIsInternational] = useState([false, false, false]);
|
||||||
|
|
||||||
const baseUrl = "https://db-mediahub.polri.go.id/";
|
const baseUrl = "https://analytic.sitani.info/";
|
||||||
const url = "https://db-mediahub.polri.go.id/trusted/";
|
const url = "https://analytic.sitani.info/trusted/";
|
||||||
|
|
||||||
const view1 =
|
const view1 =
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
|
|
@ -182,15 +182,15 @@ export default function ExecutiveDashboard() {
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
? isInternational[1]
|
? isInternational[1]
|
||||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
|
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
|
||||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-published-produksi-executive?"
|
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-executive?"
|
||||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-konten-publisher-polda-executive?provinsi-polda=${poldaState}&`;
|
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/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_Rev202/db-waktu-akses-pengguna-executive?"
|
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-executive?"
|
||||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev202/db-waktu-akses-pengguna-polda-executive?provinsi-polda=${poldaState}&`;
|
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?provinsi-polda=${poldaState}&`;
|
||||||
|
|
||||||
const view4 =
|
const view4 =
|
||||||
levelName == "MABES POLRI"
|
levelName == "MABES POLRI"
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import BlogTable from "../contributor/blog/components/blog-table";
|
||||||
import ContentTable from "./routine-task/components/content-table";
|
import ContentTable from "./routine-task/components/content-table";
|
||||||
import RecentActivity from "./routine-task/components/recent-activity";
|
import RecentActivity from "./routine-task/components/recent-activity";
|
||||||
import { Link } from "@/components/navigation";
|
import { Link } from "@/components/navigation";
|
||||||
|
import ReportTable from "../contributor/report/components/report-table";
|
||||||
|
|
||||||
const DashboardPage = () => {
|
const DashboardPage = () => {
|
||||||
const t = useTranslations("AnalyticsDashboard");
|
const t = useTranslations("AnalyticsDashboard");
|
||||||
|
|
@ -59,6 +60,12 @@ const DashboardPage = () => {
|
||||||
>
|
>
|
||||||
{t("indeks")}
|
{t("indeks")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="report"
|
||||||
|
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||||
|
>
|
||||||
|
{t("report")}
|
||||||
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Card>
|
</Card>
|
||||||
<TabsContent value="routine-task">
|
<TabsContent value="routine-task">
|
||||||
|
|
@ -107,7 +114,7 @@ const DashboardPage = () => {
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center">
|
<CardHeader className="flex flex-row items-center">
|
||||||
<CardTitle className="flex-1">{t("tabel")}</CardTitle>
|
<CardTitle className="flex-1">{t("tabel")}</CardTitle>
|
||||||
<DashboardDropdown />
|
{/* <DashboardDropdown /> */}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ContentTable />
|
<ContentTable />
|
||||||
|
|
@ -157,7 +164,7 @@ const DashboardPage = () => {
|
||||||
<div className="grid grid-cols-12 gap-5">
|
<div className="grid grid-cols-12 gap-5">
|
||||||
<div className="lg:col-span-12 col-span-12">
|
<div className="lg:col-span-12 col-span-12">
|
||||||
<Card>
|
<Card>
|
||||||
<Card className="py-4 px-3">
|
{/* <Card className="py-4 px-3">
|
||||||
<div className="flex flex-col md:flex-row md:justify-between md:items-center lg:flex-row lg:justify-between lg:items-center">
|
<div className="flex flex-col md:flex-row md:justify-between md:items-center lg:flex-row lg:justify-between lg:items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900 mb-2">
|
<div className="flex-1 text-xl font-medium text-default-900 mb-2">
|
||||||
Table Indeks
|
Table Indeks
|
||||||
|
|
@ -171,7 +178,7 @@ const DashboardPage = () => {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card> */}
|
||||||
<CardContent className="p-0 mt-3">
|
<CardContent className="p-0 mt-3">
|
||||||
<BlogTable />
|
<BlogTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
@ -179,6 +186,17 @@ const DashboardPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
<TabsContent value="report">
|
||||||
|
<div className="grid grid-cols-12 gap-5">
|
||||||
|
<div className="lg:col-span-12 col-span-12">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-0 mt-3">
|
||||||
|
<ReportTable />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const CommunicationPage = () => {
|
||||||
{tab === "Pertanyaan Internal" && (
|
{tab === "Pertanyaan Internal" && (
|
||||||
<Link href="/shared/communication/internal/create">
|
<Link href="/shared/communication/internal/create">
|
||||||
<Button color="primary" size="md">
|
<Button color="primary" size="md">
|
||||||
<PlusIcon />
|
<PlusIcon size={18} className="mr-2" />
|
||||||
{t("new-question")}
|
{t("new-question")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -32,7 +32,7 @@ const CommunicationPage = () => {
|
||||||
{tab === "Kolaborasi" && (
|
{tab === "Kolaborasi" && (
|
||||||
<Link href="/shared/communication/collaboration/create">
|
<Link href="/shared/communication/collaboration/create">
|
||||||
<Button color="primary" size="md">
|
<Button color="primary" size="md">
|
||||||
<PlusIcon />
|
<PlusIcon size={18} className="mr-2" />
|
||||||
{t("new-collaboration")}
|
{t("new-collaboration")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
|
ChevronDown,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Eye,
|
Eye,
|
||||||
|
|
@ -43,6 +44,14 @@ 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";
|
import useTableColumns from "./columns";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
const TaskTable = () => {
|
const TaskTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -56,14 +65,16 @@ const TaskTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
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 [limit, setLimit] = React.useState(10);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const columns = useTableColumns();
|
const columns = useTableColumns();
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -95,15 +106,30 @@ const TaskTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit, search]);
|
}, [page, showData, search, statusFilter]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await listContest(search, limit, page - 1);
|
const res = await listContest(search, showData, page - 1);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
let contentData = data?.content;
|
||||||
|
|
||||||
|
if (statusFilter.length > 0) {
|
||||||
|
contentData = contentData.filter((item: any) => {
|
||||||
|
const { isPublishForAll, isPublishForMabes } = item;
|
||||||
|
|
||||||
|
const status = (() => {
|
||||||
|
if (isPublishForAll && isPublishForMabes) return 1; // Publish
|
||||||
|
if (!isPublishForAll && isPublishForMabes) return 3; // Terkirim
|
||||||
|
return 2; // Pending
|
||||||
|
})();
|
||||||
|
|
||||||
|
return statusFilter.includes(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
@ -116,6 +142,14 @@ const TaskTable = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleStatusCheckboxChange = (status: number) => {
|
||||||
|
setStatusFilter((prev) =>
|
||||||
|
prev.includes(status)
|
||||||
|
? prev.filter((item) => item !== status)
|
||||||
|
: [...prev, status]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value);
|
setSearch(e.target.value);
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
|
@ -123,8 +157,8 @@ const TaskTable = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center px-5">
|
<div className="flex flex-col sm:flex-row md:flex-row lg:flex-row justify-between items-center px-3 gap-y-2">
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="w-full sm:w-[150px] md:w-[250px] lg:w-[250px]">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -138,17 +172,90 @@ const TaskTable = () => {
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
<div className="flex flex-row">
|
||||||
<Input
|
<div className="mx-3">
|
||||||
placeholder="Filter Status..."
|
<DropdownMenu>
|
||||||
value={
|
<DropdownMenuTrigger asChild>
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
<Button size="md" variant="outline">
|
||||||
}
|
1 - {showData} Data
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
</Button>
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
</DropdownMenuTrigger>
|
||||||
}
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
className="max-w-sm "
|
<DropdownMenuRadioGroup
|
||||||
/>
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="100">
|
||||||
|
1 - 100 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="250">
|
||||||
|
1 - 250 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" className="ml-auto" size="md">
|
||||||
|
Filter <ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-64 h-[150px] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
|
<p>Filter</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-1"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-1" className="text-sm">
|
||||||
|
Publish
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-2"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(2)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-2" className="text-sm">
|
||||||
|
Pending
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center px-4 py-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="status-3"
|
||||||
|
className="mr-2"
|
||||||
|
checked={statusFilter.includes(3)}
|
||||||
|
onChange={() => handleStatusCheckboxChange(3)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="status-3" className="text-sm">
|
||||||
|
Terkirim
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const ContestPage = () => {
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/shared/contest/create"}>
|
<Link href={"/shared/contest/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-contest")}
|
{t("create-contest")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -113,19 +113,18 @@ const CuratedContentPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-5 pb-3">
|
<div className="ml-5 pb-3">
|
||||||
<Label>Audio Visual</Label>
|
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<VideoSliderPage />
|
<VideoSliderPage />
|
||||||
</div>
|
</div>
|
||||||
<Label>Audio</Label>
|
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<AudioSliderPage />
|
<AudioSliderPage />
|
||||||
</div>
|
</div>
|
||||||
<Label>Foto</Label>
|
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<ImageSliderPage />
|
<ImageSliderPage />
|
||||||
</div>
|
</div>
|
||||||
<Label>Teks</Label>
|
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<TeksSliderPage />
|
<TeksSliderPage />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,7 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
ColumnDef,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
|
|
||||||
import {
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -27,50 +19,60 @@ const columns: ColumnDef<any>[] = [
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "question",
|
accessorKey: "report",
|
||||||
header: "Question",
|
header: "Pelapor",
|
||||||
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
cell: ({ row }) => <span>{row.getValue("report")}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "answer",
|
accessorKey: "reportDetail",
|
||||||
header: "Answer",
|
header: "Detail Laporan",
|
||||||
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
cell: ({ row }) => <span>{row.getValue("reportDetail")}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
accessorKey: "reportAccount",
|
||||||
accessorKey: "action",
|
header: "Terlapor",
|
||||||
header: "Actions",
|
cell: ({ row }) => <span>{row.getValue("reportAccount")}</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">
|
|
||||||
<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 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>
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("status")}</span>,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// 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">
|
||||||
|
// <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 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>
|
||||||
|
// <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;
|
export default columns;
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ import TablePagination from "@/components/table/table-pagination";
|
||||||
import { getFaqList } from "@/service/master/faq";
|
import { getFaqList } from "@/service/master/faq";
|
||||||
import columns from "./column";
|
import columns from "./column";
|
||||||
|
|
||||||
const FaqTable = () => {
|
const ReportTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -94,11 +94,11 @@ const FaqTable = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get('page');
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
setPage(Number(pageFromUrl));
|
setPage(Number(pageFromUrl));
|
||||||
}
|
}
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
|
|
@ -191,9 +191,14 @@ const FaqTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
visiblePageCount={5}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaqTable;
|
export default ReportTable;
|
||||||
|
|
|
||||||
|
|
@ -3,35 +3,44 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import FaqTable from "./components/table";
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
|
import { StatisticsBlock } from "@/components/blocks/statistics-block";
|
||||||
|
import ReportTable from "./components/table";
|
||||||
|
|
||||||
const FaqPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
|
Pelaporan Akun
|
||||||
|
</div>
|
||||||
|
<div className="flex-none"></div>
|
||||||
|
</div>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
<CardContent className="p-4">
|
||||||
<CardTitle>
|
<div className="grid md:grid-cols-3 gap-4">
|
||||||
<div className="flex items-center">
|
<StatisticsBlock
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
total="64"
|
||||||
FAQ Data
|
title="Total Pelaporan"
|
||||||
</div>
|
className=" border shadow-none"
|
||||||
<div className="flex-none">
|
/>
|
||||||
<Button
|
<StatisticsBlock
|
||||||
fullWidth
|
title="Total Disetujui"
|
||||||
size="md"
|
total="564"
|
||||||
>
|
className=" border shadow-none"
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
chartColor="#FB8F65"
|
||||||
New FAQ
|
/>
|
||||||
</Button>
|
<StatisticsBlock
|
||||||
</div>
|
title="Pelaporan Pending"
|
||||||
</div>
|
total="+5.0%"
|
||||||
</CardTitle>
|
className=" border shadow-none"
|
||||||
</CardHeader>
|
chartColor="#2563eb"
|
||||||
<CardContent className="p-0">
|
/>
|
||||||
<FaqTable />
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
<ReportTable />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Nama",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "phoneNumber",
|
||||||
|
header: "No.Telp",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdBy = row.original.createdBy; // Akses properti category
|
||||||
|
return (
|
||||||
|
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "email",
|
||||||
|
header: "Email",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const sendTo = row.original.sendTo; // Akses properti category
|
||||||
|
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdName",
|
||||||
|
header: "Admin",
|
||||||
|
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: "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={`/supervisor/communications/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={`/supervisor/communications/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;
|
||||||
|
|
@ -0,0 +1,299 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
UploadIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./columns";
|
||||||
|
import {
|
||||||
|
listDataAudio,
|
||||||
|
listDataImage,
|
||||||
|
listDataVideo,
|
||||||
|
} from "@/service/content/content";
|
||||||
|
import { listTicketingInternal } from "@/service/communication/communication";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
import { Card } from "nextra-theme-docs";
|
||||||
|
import { CardContent } from "@/components/ui/card";
|
||||||
|
|
||||||
|
const ContactTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
|
const [activeCategory, setActiveCategory] = React.useState<string | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
// React.useEffect(() => {
|
||||||
|
// fetchData();
|
||||||
|
// setPagination({
|
||||||
|
// pageIndex: 0,
|
||||||
|
// pageSize: Number(showData),
|
||||||
|
// });
|
||||||
|
// }, [page, showData]);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (activeCategory) {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [page, showData, activeCategory]);
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// async function fetchData() {
|
||||||
|
// try {
|
||||||
|
// const res = await listTicketingInternal(
|
||||||
|
// page - 1,
|
||||||
|
// Number(showData),
|
||||||
|
// search
|
||||||
|
// );
|
||||||
|
// const data = res?.data?.data;
|
||||||
|
// const contentData = data?.content;
|
||||||
|
// contentData.forEach((item: any, index: number) => {
|
||||||
|
// item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
// setDataTable(contentData);
|
||||||
|
// setTotalData(data?.totalElements);
|
||||||
|
// setTotalPage(data?.totalPages);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Error fetching tasks:", error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await listTicketingInternal(
|
||||||
|
page - 1,
|
||||||
|
Number(showData),
|
||||||
|
search,
|
||||||
|
activeCategory
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content || [];
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto ">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
</InputGroupText>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search Judul..."
|
||||||
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center justify-around my-3">
|
||||||
|
{["Polda", "Satker Humas", "Satker Mabes"].map((category, index) => (
|
||||||
|
<CardContent
|
||||||
|
key={index}
|
||||||
|
className="shadow-lg border p-2 cursor-pointer"
|
||||||
|
onClick={() => setActiveCategory(category)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-3 text-center py-5 w-[250px]">
|
||||||
|
<p className="text-3xl font-bold">
|
||||||
|
{index === 0 ? 34 : index === 1 ? 1 : 43}
|
||||||
|
</p>
|
||||||
|
<p>{category}</p>
|
||||||
|
<p className="text-blue-700">Lihat Kontak</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{activeCategory && (
|
||||||
|
<div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactTable;
|
||||||
|
|
@ -0,0 +1,359 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} 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";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
name: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
username: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
password: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
phoneNumber: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
skills: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
experiences: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
company: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Placements = {
|
||||||
|
index: number;
|
||||||
|
roleId?: string;
|
||||||
|
userLevelId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AddContactForm() {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<z.infer<typeof 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: "Polda",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "12",
|
||||||
|
name: "Satker Humas",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "13",
|
||||||
|
name: "Satker Mabes",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Simpan Data",
|
||||||
|
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
save(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
console.log("data", data);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function successSubmit() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/admin/add-experts");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-3 bg-white rounded-sm p-4"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Lengkap</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phoneNumber"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>No. HP</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-2 mb-0 pb-0">
|
||||||
|
<FormLabel>
|
||||||
|
Kategori Akun <span className="text-red-500">*</span>
|
||||||
|
</FormLabel>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username Admin</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username Approver</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username Kontributor</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-end gap-2 mt-4 pt-4">
|
||||||
|
<Link href="/supervisor/communications/contact">
|
||||||
|
<Button
|
||||||
|
size="md"
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
color="destructive"
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Nama",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "phoneNumber",
|
||||||
|
header: "No.Telp",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdBy = row.original.createdBy; // Akses properti category
|
||||||
|
return (
|
||||||
|
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "email",
|
||||||
|
header: "Email",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const sendTo = row.original.sendTo; // Akses properti category
|
||||||
|
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdName",
|
||||||
|
header: "Admin",
|
||||||
|
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: "approver",
|
||||||
|
header: "Approver",
|
||||||
|
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: "contributor",
|
||||||
|
header: "Contributor",
|
||||||
|
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: "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={`/supervisor/communications/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={`/supervisor/communications/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;
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
UploadIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./columns";
|
||||||
|
import {
|
||||||
|
listDataAudio,
|
||||||
|
listDataImage,
|
||||||
|
listDataVideo,
|
||||||
|
} from "@/service/content/content";
|
||||||
|
import { listTicketingInternal } from "@/service/communication/communication";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
import { Card } from "nextra-theme-docs";
|
||||||
|
import { CardContent } from "@/components/ui/card";
|
||||||
|
|
||||||
|
const ImportTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
|
const [activeCategory, setActiveCategory] = React.useState<string | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const [selectedFile, setSelectedFile] = React.useState(null);
|
||||||
|
|
||||||
|
const handleFileChange = (event: any) => {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
setSelectedFile(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
// React.useEffect(() => {
|
||||||
|
// fetchData();
|
||||||
|
// setPagination({
|
||||||
|
// pageIndex: 0,
|
||||||
|
// pageSize: Number(showData),
|
||||||
|
// });
|
||||||
|
// }, [page, showData]);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (activeCategory) {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [page, showData, activeCategory]);
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// async function fetchData() {
|
||||||
|
// try {
|
||||||
|
// const res = await listTicketingInternal(
|
||||||
|
// page - 1,
|
||||||
|
// Number(showData),
|
||||||
|
// search
|
||||||
|
// );
|
||||||
|
// const data = res?.data?.data;
|
||||||
|
// const contentData = data?.content;
|
||||||
|
// contentData.forEach((item: any, index: number) => {
|
||||||
|
// item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
// setDataTable(contentData);
|
||||||
|
// setTotalData(data?.totalElements);
|
||||||
|
// setTotalPage(data?.totalPages);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Error fetching tasks:", error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await listTicketingInternal(
|
||||||
|
page - 1,
|
||||||
|
Number(showData),
|
||||||
|
search,
|
||||||
|
activeCategory
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content || [];
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto ">
|
||||||
|
<div className="flex">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
className="p-2 border rounded-md"
|
||||||
|
/>
|
||||||
|
{/* {selectedFile && (
|
||||||
|
<p className="text-sm text-gray-600">File: {selectedFile.name}</p>
|
||||||
|
)} */}
|
||||||
|
</div>
|
||||||
|
<div className="my-3">
|
||||||
|
<Button type="button" variant={"outline"} color="success">
|
||||||
|
Import Data
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="my-3">
|
||||||
|
<Link href="/supervisor/communications/contact">
|
||||||
|
<Button variant={"outline"} color="primary">
|
||||||
|
Kembali
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImportTable;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { PlusIcon, User } from "lucide-react";
|
||||||
|
import ContactTable from "../components/contact-table";
|
||||||
|
import ImportTable from "./components/contact-table";
|
||||||
|
|
||||||
|
const ImportPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="w-full overflow-x-auto p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between py-3 border rounded-md px-3">
|
||||||
|
<p className="text-lg font-semibold">Kontak</p>
|
||||||
|
</div>
|
||||||
|
<ImportTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImportPage;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { PlusIcon, User } from "lucide-react";
|
||||||
|
import InternalSpvTable from "../internal/components/internal-table";
|
||||||
|
import ContactTable from "./components/contact-table";
|
||||||
|
|
||||||
|
const ContactPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="w-full overflow-x-auto p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between py-3 border rounded-md px-3">
|
||||||
|
<p className="text-lg font-semibold">Kontak</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between py-3">
|
||||||
|
<p className="text-lg pl-3 font-semibold">Semua Kontak</p>
|
||||||
|
<div>
|
||||||
|
<Link href="/supervisor/communications/contact/import-contact">
|
||||||
|
<Button color="success" size="md">
|
||||||
|
<PlusIcon />
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
</Link>{" "}
|
||||||
|
<Link href="/supervisor/communications/contact/create">
|
||||||
|
<Button color="primary" size="md">
|
||||||
|
<User />
|
||||||
|
Tambah Kontak
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ContactTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactPage;
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Search } from "lucide-react";
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
{ name: "Miles Esther", initials: "ME", time: "06.00" },
|
||||||
|
{ name: "Flores Juanita", initials: "FJ", time: "06.00" },
|
||||||
|
{ name: "Henry Arthur", initials: "HA", time: "06.00" },
|
||||||
|
{ name: "Polres Jakarta Selatan", initials: "PJ", time: "06.00" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const WebChatPage = () => {
|
||||||
|
const [selectedChat, setSelectedChat] = useState<any>(null);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
{!selectedChat ? (
|
||||||
|
// Tampilan daftar pesan
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between py-3 border rounded-md px-3">
|
||||||
|
<p className="text-lg font-semibold">Web Chat</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row justify-start rounded-lg ">
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<Input placeholder="Cari..." className=" w-[200px] mr-3" />
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="default" size="md" color="primary">
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="max-w-[350px] bg-black text-white rounded-lg p-4">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-lg font-bold text-white">
|
||||||
|
New Chat
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="relative">
|
||||||
|
<Search
|
||||||
|
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
||||||
|
size={16}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Search here..."
|
||||||
|
className="w-full pl-10 text-white placeholder-gray-400 border-none rounded-md focus:ring-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<p className="text-sm font-semibold">+ Group Chat</p>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<p className="text-sm text-gray-400">
|
||||||
|
Frequently contacted
|
||||||
|
</p>
|
||||||
|
<div className="mt-2 space-y-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="w-10 h-10 flex items-center justify-center bg-white text-black font-bold rounded-full">
|
||||||
|
ME
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
Miles Esther
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="w-10 h-10 flex items-center justify-center bg-white text-black font-bold rounded-full">
|
||||||
|
HA
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
Henry Arthur
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-4 bg-gray-100 rounded-lg">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{messages.map((msg: any, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex items-center justify-between p-4 bg-white rounded-lg shadow cursor-pointer hover:bg-gray-200"
|
||||||
|
onClick={() => setSelectedChat(msg)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="w-12 h-12 flex items-center justify-center bg-black text-white rounded-full text-lg font-bold">
|
||||||
|
{msg.initials}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-bold text-lg">{msg.name}</p>
|
||||||
|
<p className="text-gray-600 text-sm">
|
||||||
|
✅ Hallo, untuk patroli hari ini sudah bisa di akses
|
||||||
|
di daily tasks, silahkan anda periksa
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-500 text-sm">{msg.time}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
// Tampilan chat
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between py-3 border rounded-md px-3 my-3">
|
||||||
|
<p className="text-lg font-semibold">Web Chat</p>
|
||||||
|
</div>
|
||||||
|
<Card className="h-[100vh]">
|
||||||
|
<CardContent className="p-0 flex flex-col h-full">
|
||||||
|
{/* Header Chat */}
|
||||||
|
<div className="flex items-center bg-black text-white p-4">
|
||||||
|
<button
|
||||||
|
className="mr-4 text-white text-xl"
|
||||||
|
onClick={() => setSelectedChat(null)}
|
||||||
|
>
|
||||||
|
←
|
||||||
|
</button>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="w-10 h-10 flex items-center justify-center bg-white text-black rounded-full text-lg font-bold">
|
||||||
|
{selectedChat.initials}
|
||||||
|
</div>
|
||||||
|
<p className="font-bold text-lg">{selectedChat.name}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Body Chat */}
|
||||||
|
<div className="flex-1 bg-gray-300 p-4 flex flex-col justify-end">
|
||||||
|
<p className="text-center text-gray-500 mb-4">Today</p>
|
||||||
|
|
||||||
|
{/* Pesan masuk */}
|
||||||
|
<div className="flex items-start mb-4">
|
||||||
|
<div className="bg-white p-3 rounded-lg shadow max-w-xs">
|
||||||
|
Hallo, untuk patroli hari ini sudah bisa di akses di daily
|
||||||
|
tasks, silahkan anda periksa
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-500 text-xs ml-2">06.00</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pesan keluar */}
|
||||||
|
<div className="flex items-end justify-end mb-4">
|
||||||
|
<p className="text-gray-500 text-xs mr-2">06.00</p>
|
||||||
|
<div className="bg-blue-500 text-white p-3 rounded-lg shadow max-w-xs">
|
||||||
|
Hallo, bisakah mengirimkan rute patroli untuk hari ini?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pesan keluar lainnya */}
|
||||||
|
<div className="flex items-end justify-end mb-4">
|
||||||
|
<p className="text-gray-500 text-xs mr-2">06.00</p>
|
||||||
|
<div className="bg-blue-500 text-white p-3 rounded-lg shadow max-w-xs">
|
||||||
|
Terima kasih banyak
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Input Chat */}
|
||||||
|
<div className="flex items-center p-4 bg-black">
|
||||||
|
<Button className="bg-white text-black p-2 rounded-full mr-2">
|
||||||
|
+
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
placeholder="Tulis pesan..."
|
||||||
|
className="flex-1 rounded-lg"
|
||||||
|
/>
|
||||||
|
<Button className="ml-2 bg-white text-black p-2 rounded-full">
|
||||||
|
🎤
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WebChatPage;
|
||||||
|
|
@ -8,13 +8,29 @@ import NewContent from "@/components/landing-page/new-content";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading, successCallback, warning } from "@/config/swal";
|
import { close, error, loading, successCallback, warning } from "@/config/swal";
|
||||||
import { checkWishlistStatus, createPublicSuggestion, deletePublicSuggestion, deleteWishlist, getDetail, getPublicSuggestionList, saveWishlist } from "@/service/landing/landing";
|
import {
|
||||||
|
checkWishlistStatus,
|
||||||
|
createPublicSuggestion,
|
||||||
|
deletePublicSuggestion,
|
||||||
|
deleteWishlist,
|
||||||
|
getDetail,
|
||||||
|
getPublicSuggestionList,
|
||||||
|
saveWishlist,
|
||||||
|
} from "@/service/landing/landing";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
import { checkMaliciousText, formatDateToIndonesian, getPublicLocaleTimestamp } from "@/utils/globals";
|
import {
|
||||||
|
checkMaliciousText,
|
||||||
|
formatDateToIndonesian,
|
||||||
|
getPublicLocaleTimestamp,
|
||||||
|
} from "@/utils/globals";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import parse from "html-react-parser";
|
import parse from "html-react-parser";
|
||||||
|
|
@ -22,7 +38,6 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
|
|
||||||
interface Size {
|
interface Size {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
|
@ -64,8 +79,11 @@ const DetailInfo = () => {
|
||||||
const poldaName = params?.polda_name;
|
const poldaName = params?.polda_name;
|
||||||
const satkerName = params?.satker_name;
|
const satkerName = params?.satker_name;
|
||||||
|
|
||||||
let prefixPath = poldaName ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` : "/";
|
let prefixPath = poldaName
|
||||||
|
? `/polda/${poldaName}`
|
||||||
|
: satkerName
|
||||||
|
? `/satker/${satkerName}`
|
||||||
|
: "/";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
|
|
@ -178,14 +196,14 @@ const DetailInfo = () => {
|
||||||
const sizes: Size[] = Object.entries(scaleFactors).map(([label, factor]) => {
|
const sizes: Size[] = Object.entries(scaleFactors).map(([label, factor]) => {
|
||||||
const width = Number(main?.widthPixel);
|
const width = Number(main?.widthPixel);
|
||||||
const height = Number(main?.heightPixel);
|
const height = Number(main?.heightPixel);
|
||||||
|
|
||||||
if (isNaN(width) || isNaN(height) || width === 0) {
|
if (isNaN(width) || isNaN(height) || width === 0) {
|
||||||
return { label, value: "Invalid size" };
|
return { label, value: "Invalid size" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const newWidth = Math.round(width * factor);
|
const newWidth = Math.round(width * factor);
|
||||||
const newHeight = Math.round((width * factor) / (width / height));
|
const newHeight = Math.round((width * factor) / (width / height));
|
||||||
|
|
||||||
return { label, value: `${newWidth} x ${newHeight} px` };
|
return { label, value: `${newWidth} x ${newHeight} px` };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -263,7 +281,8 @@ const DetailInfo = () => {
|
||||||
|
|
||||||
xhr.addEventListener("readystatechange", () => {
|
xhr.addEventListener("readystatechange", () => {
|
||||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||||
const contentType = xhr.getResponseHeader("content-type") || "application/octet-stream";
|
const contentType =
|
||||||
|
xhr.getResponseHeader("content-type") || "application/octet-stream";
|
||||||
const extension = contentType.split("/")[1];
|
const extension = contentType.split("/")[1];
|
||||||
const filename = `${name}.${extension}`;
|
const filename = `${name}.${extension}`;
|
||||||
|
|
||||||
|
|
@ -297,7 +316,11 @@ const DetailInfo = () => {
|
||||||
if (type == "wa" && width <= 768) {
|
if (type == "wa" && width <= 768) {
|
||||||
window.open(`whatsapp://send?${url}`, "_blank");
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
} else if (type == "wa" && width > 768) {
|
} else if (type == "wa" && width > 768) {
|
||||||
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
window.open(
|
||||||
|
`https://web.whatsapp.com/send?${url}`,
|
||||||
|
"_blank",
|
||||||
|
"noreferrer"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
window.open(url);
|
window.open(url);
|
||||||
}
|
}
|
||||||
|
|
@ -386,7 +409,9 @@ const DetailInfo = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendSuggestionChild(parentId: any) {
|
async function sendSuggestionChild(parentId: any) {
|
||||||
const inputElement = document.querySelector(`#input-comment-${parentId}`) as HTMLInputElement;
|
const inputElement = document.querySelector(
|
||||||
|
`#input-comment-${parentId}`
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
if (inputElement && inputElement.value.length > 3) {
|
if (inputElement && inputElement.value.length > 3) {
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -399,7 +424,9 @@ const DetailInfo = () => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
const response = await createPublicSuggestion(data);
|
const response = await createPublicSuggestion(data);
|
||||||
console.log(response);
|
console.log(response);
|
||||||
const responseGet: any = await getPublicSuggestionList(slug?.split("-")?.[0]);
|
const responseGet: any = await getPublicSuggestionList(
|
||||||
|
slug?.split("-")?.[0]
|
||||||
|
);
|
||||||
console.log(responseGet.data?.data);
|
console.log(responseGet.data?.data);
|
||||||
setListSuggestion(responseGet.data?.data);
|
setListSuggestion(responseGet.data?.data);
|
||||||
|
|
||||||
|
|
@ -477,9 +504,11 @@ const DetailInfo = () => {
|
||||||
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
|
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
|
||||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
const toBase64 = (str: string) =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? Buffer.from(str).toString("base64")
|
||||||
|
: window.btoa(str);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -494,7 +523,16 @@ const DetailInfo = () => {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={2560} height={1440} src={detailDataImage?.files[selectedImage]?.url} alt="Main" className="rounded-lg w-auto h-fit" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={2560}
|
||||||
|
height={1440}
|
||||||
|
src={detailDataImage?.files[selectedImage]?.url}
|
||||||
|
alt="Main"
|
||||||
|
className="rounded-lg w-auto h-fit"
|
||||||
|
/>
|
||||||
<div className="absolute top-4 left-4"></div>
|
<div className="absolute top-4 left-4"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -510,7 +548,16 @@ const DetailInfo = () => {
|
||||||
<div className="py-4 flex flex-row gap-3">
|
<div className="py-4 flex flex-row gap-3">
|
||||||
{detailDataImage?.files?.map((file: any, index: number) => (
|
{detailDataImage?.files?.map((file: any, index: number) => (
|
||||||
<a onClick={() => setSelectedImage(index)} key={file?.id}>
|
<a onClick={() => setSelectedImage(index)} key={file?.id}>
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1920} height={1080} alt="image-small" src={file?.url} className="w-[120px] h-[90px] object-cover rounded-md cursor-pointer hover:ring-2 hover:ring-red-600" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={1920}
|
||||||
|
height={1080}
|
||||||
|
alt="image-small"
|
||||||
|
src={file?.url}
|
||||||
|
className="w-[120px] h-[90px] object-cover rounded-md cursor-pointer hover:ring-2 hover:ring-red-600"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -520,7 +567,10 @@ const DetailInfo = () => {
|
||||||
<div className="text-gray-500 flex flex-col lg:flex-row justify-between items-center border-t mt-4">
|
<div className="text-gray-500 flex flex-col lg:flex-row justify-between items-center border-t mt-4">
|
||||||
<div className="flex flex-col lg:flex-row items-center mt-3 lg:justify-between">
|
<div className="flex flex-col lg:flex-row items-center mt-3 lg:justify-between">
|
||||||
<p className="text-xs lg:text-sm">
|
<p className="text-xs lg:text-sm">
|
||||||
{t("by")} <span className="font-semibold text-black dark:text-white">{detailDataImage?.uploadedBy?.userLevel?.name}</span>
|
{t("by")}
|
||||||
|
<span className="font-semibold text-black dark:text-white">
|
||||||
|
{detailDataImage?.uploadedBy?.userLevel?.name}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{/* <p className="text-xs lg:text-sm">
|
{/* <p className="text-xs lg:text-sm">
|
||||||
| {t("updatedOn")}
|
| {t("updatedOn")}
|
||||||
|
|
@ -528,7 +578,10 @@ const DetailInfo = () => {
|
||||||
</p> */}
|
</p> */}
|
||||||
<p className="text-xs lg:text-sm">
|
<p className="text-xs lg:text-sm">
|
||||||
| {t("updatedOn")}
|
| {t("updatedOn")}
|
||||||
{formatDateToIndonesian(new Date(detailDataImage?.updatedAt))} {"WIB"}
|
{formatDateToIndonesian(
|
||||||
|
new Date(detailDataImage?.updatedAt)
|
||||||
|
)}{" "}
|
||||||
|
{"WIB"}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs lg:text-sm flex justify-center items-center">
|
<p className="text-xs lg:text-sm flex justify-center items-center">
|
||||||
|
|
|
|
||||||
|
|
@ -546,51 +599,86 @@ const DetailInfo = () => {
|
||||||
|
|
||||||
{/* Keterangan */}
|
{/* Keterangan */}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<h1 className="flex flex-row font-bold text-lg lg:text-2xl my-8">{detailDataImage?.title}</h1>
|
<h1 className="flex flex-row font-bold text-lg lg:text-2xl my-8">
|
||||||
<div className="font-light text-justify mb-5 space-y-4 lg:mb-0" dangerouslySetInnerHTML={{ __html: detailDataImage?.htmlDescription }} />
|
{detailDataImage?.title}
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
className="font-light text-justify mb-5 space-y-4 lg:mb-0"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: detailDataImage?.htmlDescription,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bagian Kanan */}
|
{/* Bagian Kanan */}
|
||||||
<div className="md:w-1/4 p-4 bg-[#f7f7f7] dark:bg-slate-600 h-fit rounded-lg mx-4">
|
<div className="md:w-1/4 p-4 bg-[#f7f7f7] dark:bg-slate-600 h-fit rounded-lg mx-4">
|
||||||
{isSaved ? (
|
{isSaved ? (
|
||||||
<a onClick={() => handleDeleteWishlist()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
|
<a
|
||||||
|
onClick={() => handleDeleteWishlist()}
|
||||||
|
className="flex flex-col mb-3 items-center justify-center cursor-pointer"
|
||||||
|
>
|
||||||
<Icon icon="material-symbols:bookmark" width={40} />
|
<Icon icon="material-symbols:bookmark" width={40} />
|
||||||
<p className="text-base lg:text-lg">{t("delete")}</p>
|
<p className="text-base lg:text-lg">{t("delete")}</p>
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<a onClick={() => doBookmark()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
|
<a
|
||||||
<Icon icon="material-symbols:bookmark-outline" width={40} />
|
onClick={() => doBookmark()}
|
||||||
<p className="text-base lg:text-lg">{t("save")}</p>
|
className="flex flex-col mb-3 items-center justify-center cursor-pointer"
|
||||||
|
>
|
||||||
|
<Icon icon="material-symbols:bookmark-outline" width={25} />
|
||||||
|
<p className="text-base lg:text-sm">{t("save")}</p>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* garis */}
|
{/* garis */}
|
||||||
<div className="border-t border-black my-4"></div>
|
<div className="border-t border-black my-4"></div>
|
||||||
|
<div className="flex flex-col justify-center items-center gap-3">
|
||||||
|
<div className="w-auto mb-3">
|
||||||
|
<Link
|
||||||
|
href={`/all/filter?title=polda&category=${detailDataImage?.category.id}`}
|
||||||
|
className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 rounded w-auto"
|
||||||
|
>
|
||||||
|
{detailDataImage?.category?.name}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Link href={`/all/filter?title=polda&category=${detailDataImage?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
|
<div className="flex justify-center flex-wrap gap-2 mb-4">
|
||||||
{detailDataImage?.category?.name}
|
{detailDataImage?.tags?.split(",").map((tag: string) => (
|
||||||
</Link>
|
<a
|
||||||
|
onClick={() => router.push(`/all/filter?tag=${tag}`)}
|
||||||
<div className="flex justify-center flex-wrap gap-2 mb-4">
|
key={tag}
|
||||||
{detailDataImage?.tags?.split(",").map((tag: string) => (
|
className="bg-gray-200 text-gray-700 text-xs px-3 py-3 font-semibold rounded-full cursor-pointer hover:bg-gray-500"
|
||||||
<a onClick={() => router.push(`/all/filter?tag=${tag}`)} key={tag} className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500">
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-black my-4"></div>
|
<div className="border-t border-black my-4"></div>
|
||||||
|
|
||||||
{/* Opsi Ukuran Foto */}
|
{/* Opsi Ukuran Foto */}
|
||||||
<h4 className="flex text-lg justify-center items-center font-semibold my-3">{t("imageSize")}</h4>
|
<h4 className="flex text-lg justify-center items-center font-semibold my-3">
|
||||||
|
{t("imageSize")}
|
||||||
|
</h4>
|
||||||
<div className="border-t border-black my-4"></div>
|
<div className="border-t border-black my-4"></div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{sizes.map((size: any) => (
|
{sizes.map((size: any) => (
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<div key={size?.label} className="items-center flex flex-row gap-2 cursor-pointer">
|
<div
|
||||||
<input type="radio" name="size" value={size?.label} checked={selectedSize === size?.label} onChange={(e) => setImageSizeSelected(e.target.value)} className="text-red-600 focus:ring-red-600" />
|
key={size?.label}
|
||||||
|
className="items-center flex flex-row gap-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="size"
|
||||||
|
value={size?.label}
|
||||||
|
checked={selectedSize === size?.label}
|
||||||
|
onChange={(e) => setImageSizeSelected(e.target.value)}
|
||||||
|
className="text-red-600 focus:ring-red-600"
|
||||||
|
/>
|
||||||
<div className="text-sm">{size?.label}</div>
|
<div className="text-sm">{size?.label}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">{size?.value}</div>
|
<div className="text-sm">{size?.value}</div>
|
||||||
|
|
@ -601,15 +689,30 @@ const DetailInfo = () => {
|
||||||
{/* Download Semua */}
|
{/* Download Semua */}
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<label className="flex items-center space-x-2 text-sm">
|
<label className="flex items-center space-x-2 text-sm">
|
||||||
<input type="checkbox" className="text-red-600 focus:ring-red-600" onChange={() => setIsDownloadAll(!isDownloadAll)} />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="text-red-600 focus:ring-red-600"
|
||||||
|
onChange={() => setIsDownloadAll(!isDownloadAll)}
|
||||||
|
/>
|
||||||
<span>{t("downloadAll")}</span>
|
<span>{t("downloadAll")}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tombol Download */}
|
{/* Tombol Download */}
|
||||||
<button onClick={handleDownload} className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700">
|
<button
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
onClick={handleDownload}
|
||||||
<path fill="white" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
|
className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="white"
|
||||||
|
d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{t("download")}
|
{t("download")}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -617,29 +720,90 @@ const DetailInfo = () => {
|
||||||
{/* Tombol Bagikan */}
|
{/* Tombol Bagikan */}
|
||||||
<div className="flex flex-row mt-5 justify-center">
|
<div className="flex flex-row mt-5 justify-center">
|
||||||
<p className="text-base font-semibold">{t("share")}</p>
|
<p className="text-base font-semibold">{t("share")}</p>
|
||||||
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
<a
|
||||||
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
className="ml-8 cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
handleShare(
|
||||||
|
"fb",
|
||||||
|
`https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="brandico:facebook"
|
||||||
|
height="20"
|
||||||
|
className="px-auto text-red-600 text-center"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
<a
|
||||||
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
className="ml-5 cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
handleShare(
|
||||||
|
"tw",
|
||||||
|
`https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="mdi:twitter"
|
||||||
|
width="23"
|
||||||
|
className="text-red-600 text-center"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
<a
|
||||||
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
className="ml-5 cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
handleShare(
|
||||||
|
"wa",
|
||||||
|
`text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="ri:whatsapp-fill"
|
||||||
|
width="23"
|
||||||
|
className="text-red-600 text-center"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
<PopoverTrigger
|
||||||
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
className="flex justify-end gap-1 cursor-pointer"
|
||||||
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
asChild
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
className="ml-5 cursor-pointer"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
href="#"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols-light:mail"
|
||||||
|
width="23"
|
||||||
|
className="text-red-600 text-center"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent>
|
<PopoverContent>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<h1 className="mb-2">{t("shareTo")}</h1>
|
<h1 className="mb-2">{t("shareTo")}</h1>
|
||||||
<div className="flex flex-col mb-2">
|
<div className="flex flex-col mb-2">
|
||||||
<p className="text-base font-semibold mb-1">{t("destinationEmail")}</p>
|
<p className="text-base font-semibold mb-1">
|
||||||
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder={t("pressEnter")} />
|
{t("destinationEmail")}
|
||||||
|
</p>
|
||||||
|
<Input
|
||||||
|
value={emailShareInput}
|
||||||
|
onChange={(event) =>
|
||||||
|
setEmailShareInput(event.target.value)
|
||||||
|
}
|
||||||
|
onKeyPress={handleEmailList}
|
||||||
|
type="email"
|
||||||
|
placeholder={t("pressEnter")}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
<Button
|
||||||
|
className="bg-blue-500 text-white p-2 w-fit rounded-lg"
|
||||||
|
onClick={() => shareToEmail()}
|
||||||
|
>
|
||||||
{t("send")}
|
{t("send")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -655,8 +819,15 @@ const DetailInfo = () => {
|
||||||
<div className="flex flex-col my-16 p-4 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
|
<div className="flex flex-col my-16 p-4 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
|
||||||
<div className="gap-5 flex flex-col px-4 lg:px-14">
|
<div className="gap-5 flex flex-col px-4 lg:px-14">
|
||||||
<p className="flex items-start text-lg">{t("comment")}</p>
|
<p className="flex items-start text-lg">{t("comment")}</p>
|
||||||
<Textarea placeholder={t("leaveComment")} className="flex w-full pb-12" onChange={getInputValue} />
|
<Textarea
|
||||||
<button onClick={() => postData()} className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-4 py-1">
|
placeholder={t("leaveComment")}
|
||||||
|
className="flex w-full pb-12"
|
||||||
|
onChange={getInputValue}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => postData()}
|
||||||
|
className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-4 py-1"
|
||||||
|
>
|
||||||
{t("send")}
|
{t("send")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -667,13 +838,29 @@ const DetailInfo = () => {
|
||||||
{listSuggestion?.map((data: any) => (
|
{listSuggestion?.map((data: any) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row mt-2 px-4 lg:px-14">
|
<div className="flex flex-row mt-2 px-4 lg:px-14">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1080} height={1080} src={data?.suggestionFrom?.profilePictureUrl} className="h-12 lg:h-16 w-12 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={1080}
|
||||||
|
height={1080}
|
||||||
|
src={data?.suggestionFrom?.profilePictureUrl}
|
||||||
|
className="h-12 lg:h-16 w-12 lg:w-16 mr-2"
|
||||||
|
onError={addDefaultProfile}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
|
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
|
||||||
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
||||||
{Number(data.suggestionFrom?.roleId) == 2 || Number(data.suggestionFrom?.roleId) == 3 || Number(data.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : data.suggestionFrom?.fullname}
|
{Number(data.suggestionFrom?.roleId) == 2 ||
|
||||||
|
Number(data.suggestionFrom?.roleId) == 3 ||
|
||||||
|
Number(data.suggestionFrom?.roleId) == 4
|
||||||
|
? "HUMAS POLRI"
|
||||||
|
: data.suggestionFrom?.fullname}
|
||||||
{getPublicLocaleTimestamp(new Date(data.createdAt))}
|
{getPublicLocaleTimestamp(new Date(data.createdAt))}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{data?.message}</p>
|
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">
|
||||||
|
{data?.message}
|
||||||
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
style={
|
style={
|
||||||
|
|
@ -686,11 +873,16 @@ const DetailInfo = () => {
|
||||||
onClick={() => showInput(`comment-id-${data.id}`)}
|
onClick={() => showInput(`comment-id-${data.id}`)}
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
>
|
>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("reply")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
{Number(data.suggestionFrom?.id) == Number(userId) || Number(userRoleId) == 2 ? (
|
{Number(data.suggestionFrom?.id) == Number(userId) ||
|
||||||
|
Number(userRoleId) == 2 ? (
|
||||||
<a onClick={() => deleteData(data.id)}>
|
<a onClick={() => deleteData(data.id)}>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("delete")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
|
|
@ -699,14 +891,25 @@ const DetailInfo = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{visibleInput === `comment-id-${data.id}` && (
|
{visibleInput === `comment-id-${data.id}` && (
|
||||||
<div id={`comment-id-${data.id}`} className="px-4 pl-[72px] lg:px-14 lg:pl-32 mt-2 ">
|
<div
|
||||||
<Textarea id={`input-comment-${data.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
|
id={`comment-id-${data.id}`}
|
||||||
|
className="px-4 pl-[72px] lg:px-14 lg:pl-32 mt-2 "
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
id={`input-comment-${data.id}`}
|
||||||
|
className="p-4 focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder={t("enterReply")}
|
||||||
|
/>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<a onClick={() => postDataChild(data.id)}>
|
<a onClick={() => postDataChild(data.id)}>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 mt-2 cursor-pointer">{t("send")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 mt-2 cursor-pointer">
|
||||||
|
{t("send")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
<a onClick={() => showInput(`comment-id-${data.id}`)}>
|
<a onClick={() => showInput(`comment-id-${data.id}`)}>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg mt-2 w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg mt-2 w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("cancel")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -716,30 +919,56 @@ const DetailInfo = () => {
|
||||||
? data.children?.map((child1: any) => (
|
? data.children?.map((child1: any) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-16 lg:pl-32">
|
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-16 lg:pl-32">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1080} height={1080} src={child1.suggestionFrom?.profilePictureUrl} onError={addDefaultProfile} alt="" className="h-10 lg:h-16 w-10 lg:w-16 mr-2" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={1080}
|
||||||
|
height={1080}
|
||||||
|
src={child1.suggestionFrom?.profilePictureUrl}
|
||||||
|
onError={addDefaultProfile}
|
||||||
|
alt=""
|
||||||
|
className="h-10 lg:h-16 w-10 lg:w-16 mr-2"
|
||||||
|
/>
|
||||||
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
|
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
|
||||||
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
||||||
{" "}
|
{" "}
|
||||||
<b>{Number(child1.suggestionFrom?.roleId) == 2 || Number(child1.suggestionFrom?.roleId) == 3 || Number(child1.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child1.suggestionFrom?.fullname}</b>{" "}
|
<b>
|
||||||
{getPublicLocaleTimestamp(new Date(child1.createdAt))}
|
{Number(child1.suggestionFrom?.roleId) == 2 ||
|
||||||
|
Number(child1.suggestionFrom?.roleId) == 3 ||
|
||||||
|
Number(child1.suggestionFrom?.roleId) == 4
|
||||||
|
? "HUMAS POLRI"
|
||||||
|
: child1.suggestionFrom?.fullname}
|
||||||
|
</b>{" "}
|
||||||
|
{getPublicLocaleTimestamp(
|
||||||
|
new Date(child1.createdAt)
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">
|
||||||
|
{parse(String(child1?.message))}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{parse(String(child1?.message))}</p>
|
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
style={
|
style={
|
||||||
Number(child1.suggestionFrom?.id) == Number(userId)
|
Number(child1.suggestionFrom?.id) ==
|
||||||
|
Number(userId)
|
||||||
? {
|
? {
|
||||||
display: "none",
|
display: "none",
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
}
|
}
|
||||||
onClick={() => showInput(`comment-id-${child1.id}`)}
|
onClick={() =>
|
||||||
|
showInput(`comment-id-${child1.id}`)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("reply")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
style={
|
style={
|
||||||
Number(child1.suggestionFrom?.id) == Number(userId)
|
Number(child1.suggestionFrom?.id) ==
|
||||||
|
Number(userId)
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
display: "none",
|
display: "none",
|
||||||
|
|
@ -747,21 +976,39 @@ const DetailInfo = () => {
|
||||||
}
|
}
|
||||||
onClick={() => deleteData(child1.id)}
|
onClick={() => deleteData(child1.id)}
|
||||||
>
|
>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("delete")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{visibleInput === `comment-id-${child1.id}` && (
|
{visibleInput === `comment-id-${child1.id}` && (
|
||||||
<div id={`comment-id-${child1.id}`} className="px-4 lg:px-14 pl-28 lg:pl-[200px]">
|
<div
|
||||||
<Textarea name="" className="mt-2 " id={`input-comment-${child1.id}`} placeholder={t("enterReply")} />
|
id={`comment-id-${child1.id}`}
|
||||||
|
className="px-4 lg:px-14 pl-28 lg:pl-[200px]"
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
name=""
|
||||||
|
className="mt-2 "
|
||||||
|
id={`input-comment-${child1.id}`}
|
||||||
|
placeholder={t("enterReply")}
|
||||||
|
/>
|
||||||
<div className="flex flex-row mt-2 gap-3">
|
<div className="flex flex-row mt-2 gap-3">
|
||||||
<a onClick={() => postDataChild(child1.id)}>
|
<a onClick={() => postDataChild(child1.id)}>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("send")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
<a onClick={() => showInput(`comment-id-${child1.id}`)}>
|
<a
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
|
onClick={() =>
|
||||||
|
showInput(`comment-id-${child1.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("cancel")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -771,30 +1018,62 @@ const DetailInfo = () => {
|
||||||
? child1.children?.map((child2: any) => (
|
? child1.children?.map((child2: any) => (
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-28 lg:pl-48">
|
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-28 lg:pl-48">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1080} height={1080} src={child2.suggestionFrom?.profilePictureUrl} className="h-9 lg:h-16 w-9 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={1080}
|
||||||
|
height={1080}
|
||||||
|
src={
|
||||||
|
child2.suggestionFrom?.profilePictureUrl
|
||||||
|
}
|
||||||
|
className="h-9 lg:h-16 w-9 lg:w-16 mr-2"
|
||||||
|
onError={addDefaultProfile}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
|
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
|
||||||
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
||||||
{" "}
|
{" "}
|
||||||
<b>{Number(child2.suggestionFrom?.roleId) == 2 || Number(child2.suggestionFrom?.roleId) == 3 || Number(child2.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child2.suggestionFrom?.fullname}</b>{" "}
|
<b>
|
||||||
{getPublicLocaleTimestamp(new Date(child2.createdAt))}
|
{Number(
|
||||||
|
child2.suggestionFrom?.roleId
|
||||||
|
) == 2 ||
|
||||||
|
Number(child2.suggestionFrom?.roleId) ==
|
||||||
|
3 ||
|
||||||
|
Number(child2.suggestionFrom?.roleId) ==
|
||||||
|
4
|
||||||
|
? "HUMAS POLRI"
|
||||||
|
: child2.suggestionFrom?.fullname}
|
||||||
|
</b>{" "}
|
||||||
|
{getPublicLocaleTimestamp(
|
||||||
|
new Date(child2.createdAt)
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-500 text-sm mb-4">
|
||||||
|
{parse(String(child2?.message))}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500 text-sm mb-4">{parse(String(child2?.message))}</p>
|
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
style={
|
style={
|
||||||
Number(child2.suggestionFrom?.id) == Number(userId)
|
Number(child2.suggestionFrom?.id) ==
|
||||||
|
Number(userId)
|
||||||
? {
|
? {
|
||||||
display: "none",
|
display: "none",
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
}
|
}
|
||||||
onClick={() => showInput(`comment-id-${child2.id}`)}
|
onClick={() =>
|
||||||
|
showInput(`comment-id-${child2.id}`)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("reply")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
style={
|
style={
|
||||||
Number(child2.suggestionFrom?.id) == Number(userId)
|
Number(child2.suggestionFrom?.id) ==
|
||||||
|
Number(userId)
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
display: "none",
|
display: "none",
|
||||||
|
|
@ -802,20 +1081,40 @@ const DetailInfo = () => {
|
||||||
}
|
}
|
||||||
onClick={() => deleteData(child2.id)}
|
onClick={() => deleteData(child2.id)}
|
||||||
>
|
>
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("delete")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{visibleInput === `comment-id-${child2.id}` && (
|
{visibleInput === `comment-id-${child2.id}` && (
|
||||||
<div id={`comment-id-${child2.id}`} className="px-4 lg:px-14 pl-40 lg:pl-[265px]">
|
<div
|
||||||
<Textarea name="" id={`input-comment-${child2.id}`} className="my-2" placeholder="Masukkan balasan anda" />
|
id={`comment-id-${child2.id}`}
|
||||||
|
className="px-4 lg:px-14 pl-40 lg:pl-[265px]"
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
name=""
|
||||||
|
id={`input-comment-${child2.id}`}
|
||||||
|
className="my-2"
|
||||||
|
placeholder="Masukkan balasan anda"
|
||||||
|
/>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<a onClick={() => postDataChild(child2.id)}>
|
<a
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
|
onClick={() => postDataChild(child2.id)}
|
||||||
|
>
|
||||||
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("send")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
<a onClick={() => showInput(`comment-id-${child2.id}`)}>
|
<a
|
||||||
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
|
onClick={() =>
|
||||||
|
showInput(`comment-id-${child2.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">
|
||||||
|
{t("cancel")}
|
||||||
|
</small>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,30 @@ import React, { useEffect, useState } from "react";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
|
import {
|
||||||
|
formatDateToIndonesian,
|
||||||
|
getOnlyDate,
|
||||||
|
getOnlyMonthAndYear,
|
||||||
|
} from "@/utils/globals";
|
||||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import { getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
|
import {
|
||||||
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
getUserLevelListByParent,
|
||||||
|
listCategory,
|
||||||
|
listData,
|
||||||
|
listDataRegional,
|
||||||
|
} from "@/service/landing/landing";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
import LandingPagination from "@/components/landing-page/pagination";
|
import LandingPagination from "@/components/landing-page/pagination";
|
||||||
import { Reveal } from "@/components/landing-page/Reveal";
|
import { Reveal } from "@/components/landing-page/Reveal";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
@ -38,8 +58,11 @@ const FilterPage = () => {
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [totalPage, setTotalPage] = React.useState<number>(1);
|
const [totalPage, setTotalPage] = 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>(
|
||||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
|
|
@ -60,7 +83,9 @@ const FilterPage = () => {
|
||||||
const [categoryFilter, setCategoryFilter] = useState<any>([]);
|
const [categoryFilter, setCategoryFilter] = useState<any>([]);
|
||||||
const [monthYearFilter, setMonthYearFilter] = useState<any>();
|
const [monthYearFilter, setMonthYearFilter] = useState<any>();
|
||||||
const [searchTitle, setSearchTitle] = useState<string>("");
|
const [searchTitle, setSearchTitle] = useState<string>("");
|
||||||
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
|
const [sortByOpt, setSortByOpt] = useState<any>(
|
||||||
|
sortBy === "popular" ? "clickCount" : "createdAt"
|
||||||
|
);
|
||||||
const isRegional = asPath?.includes("regional");
|
const isRegional = asPath?.includes("regional");
|
||||||
const isSatker = asPath?.includes("satker");
|
const isSatker = asPath?.includes("satker");
|
||||||
const [formatFilter, setFormatFilter] = useState<any>([]);
|
const [formatFilter, setFormatFilter] = useState<any>([]);
|
||||||
|
|
@ -73,6 +98,7 @@ const FilterPage = () => {
|
||||||
const [categories, setCategories] = useState([]);
|
const [categories, setCategories] = useState([]);
|
||||||
const [userLevels, setUserLevels] = useState([]);
|
const [userLevels, setUserLevels] = useState([]);
|
||||||
const t = useTranslations("FilterPage");
|
const t = useTranslations("FilterPage");
|
||||||
|
const [isFilterOpen, setIsFilterOpen] = useState(true);
|
||||||
|
|
||||||
// const [startDate, endDate] = dateRange;
|
// const [startDate, endDate] = dateRange;
|
||||||
|
|
||||||
|
|
@ -105,8 +131,14 @@ const FilterPage = () => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (categorie) {
|
if (categorie) {
|
||||||
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
|
setCategoryFilter(
|
||||||
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
|
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"Kategori",
|
||||||
|
categorie,
|
||||||
|
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [categorie]);
|
}, [categorie]);
|
||||||
|
|
||||||
|
|
@ -124,7 +156,19 @@ const FilterPage = () => {
|
||||||
}
|
}
|
||||||
console.log(monthYearFilter, "monthFilter");
|
console.log(monthYearFilter, "monthFilter");
|
||||||
initState();
|
initState();
|
||||||
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
|
}, [
|
||||||
|
change,
|
||||||
|
asPath,
|
||||||
|
monthYearFilter,
|
||||||
|
page,
|
||||||
|
sortBy,
|
||||||
|
sortByOpt,
|
||||||
|
title,
|
||||||
|
startDateString,
|
||||||
|
endDateString,
|
||||||
|
categorie,
|
||||||
|
formatFilter,
|
||||||
|
]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const category = await listCategory("1");
|
const category = await listCategory("1");
|
||||||
|
|
@ -147,7 +191,10 @@ const FilterPage = () => {
|
||||||
async function getDataAll() {
|
async function getDataAll() {
|
||||||
if (asPath?.includes("/polda/") == true) {
|
if (asPath?.includes("/polda/") == true) {
|
||||||
if (asPath?.split("/")[2] !== "[polda_name]") {
|
if (asPath?.split("/")[2] !== "[polda_name]") {
|
||||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
const filter =
|
||||||
|
categoryFilter?.length > 0
|
||||||
|
? categoryFilter?.sort().join(",")
|
||||||
|
: categorie || "";
|
||||||
|
|
||||||
const name = title == undefined ? "" : title;
|
const name = title == undefined ? "" : title;
|
||||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||||
|
|
@ -165,8 +212,14 @@ const FilterPage = () => {
|
||||||
filterGroup,
|
filterGroup,
|
||||||
startDateString,
|
startDateString,
|
||||||
endDateString,
|
endDateString,
|
||||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
|
monthYearFilter
|
||||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
|
? getOnlyMonthAndYear(monthYearFilter)
|
||||||
|
?.split("/")[0]
|
||||||
|
?.replace("", "")
|
||||||
|
: "",
|
||||||
|
monthYearFilter
|
||||||
|
? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1]
|
||||||
|
: "",
|
||||||
locale == "en" ? true : false
|
locale == "en" ? true : false
|
||||||
);
|
);
|
||||||
close();
|
close();
|
||||||
|
|
@ -181,7 +234,10 @@ const FilterPage = () => {
|
||||||
setTotalContent(response?.data?.data?.totalElements);
|
setTotalContent(response?.data?.data?.totalElements);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
const filter =
|
||||||
|
categoryFilter?.length > 0
|
||||||
|
? categoryFilter?.sort().join(",")
|
||||||
|
: categorie || "";
|
||||||
|
|
||||||
const name = title == undefined ? "" : title;
|
const name = title == undefined ? "" : title;
|
||||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||||
|
|
@ -198,8 +254,12 @@ const FilterPage = () => {
|
||||||
"",
|
"",
|
||||||
startDateString,
|
startDateString,
|
||||||
endDateString,
|
endDateString,
|
||||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
|
monthYearFilter
|
||||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
|
? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "")
|
||||||
|
: "",
|
||||||
|
monthYearFilter
|
||||||
|
? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1]
|
||||||
|
: "",
|
||||||
locale == "en" ? true : false
|
locale == "en" ? true : false
|
||||||
);
|
);
|
||||||
close();
|
close();
|
||||||
|
|
@ -250,7 +310,10 @@ const FilterPage = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getDataRegional() {
|
async function getDataRegional() {
|
||||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
const filter =
|
||||||
|
categoryFilter?.length > 0
|
||||||
|
? categoryFilter?.sort().join(",")
|
||||||
|
: categorie || "";
|
||||||
|
|
||||||
const name = title == undefined ? "" : title;
|
const name = title == undefined ? "" : title;
|
||||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||||
|
|
@ -263,8 +326,12 @@ const FilterPage = () => {
|
||||||
"",
|
"",
|
||||||
startDateString,
|
startDateString,
|
||||||
endDateString,
|
endDateString,
|
||||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
|
monthYearFilter
|
||||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
|
? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "")
|
||||||
|
: "",
|
||||||
|
monthYearFilter
|
||||||
|
? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1]
|
||||||
|
: "",
|
||||||
12,
|
12,
|
||||||
pages,
|
pages,
|
||||||
sortByOpt
|
sortByOpt
|
||||||
|
|
@ -381,16 +448,20 @@ const FilterPage = () => {
|
||||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
const toBase64 = (str: string) =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? Buffer.from(str).toString("base64")
|
||||||
|
: window.btoa(str);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
|
<div className="flex flex-row md:flex-row items-start gap-3 p-10 bg-[#f7f7f7] dark:bg-black">
|
||||||
|
<p> {t("image")}</p>
|
||||||
|
{">"}
|
||||||
<p>
|
<p>
|
||||||
{" "}
|
<span className="font-bold">{t("allImage")}</span>
|
||||||
{t("image")} {">"} <span className="font-bold">{t("allImage")}</span>
|
|
||||||
</p>
|
</p>
|
||||||
<p className="font-bold">|</p>
|
<p className="font-bold">|</p>
|
||||||
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableImage")}`}</p>
|
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableImage")}`}</p>
|
||||||
|
|
@ -398,106 +469,182 @@ const FilterPage = () => {
|
||||||
|
|
||||||
{/* Left */}
|
{/* Left */}
|
||||||
<div className="flex flex-col lg:flex-row gap-6 p-4">
|
<div className="flex flex-col lg:flex-row gap-6 p-4">
|
||||||
<div className="lg:w-[25%] h-fit w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
|
<div className="lg:hidden flex justify-end mb-2">
|
||||||
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
|
<button
|
||||||
<Icon icon="stash:filter-light" fontSize={30} />
|
onClick={() => setIsFilterOpen(!isFilterOpen)}
|
||||||
Filter
|
className="text-sm text-white bg-[#bb3523] px-4 py-1 rounded-md shadow"
|
||||||
</h2>
|
>
|
||||||
<div className="border-t border-black my-4 dark:border-white"></div>
|
{isFilterOpen ? "Hide Filter" : "Show Filter"}
|
||||||
<div className="space-y-6">
|
</button>
|
||||||
<div>
|
</div>
|
||||||
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
|
|
||||||
{t("search")}
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
value={searchTitle}
|
|
||||||
onChange={(e) => setSearchTitle(e.target.value)}
|
|
||||||
onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
type="text"
|
|
||||||
id="search"
|
|
||||||
placeholder={t("searchTitle")}
|
|
||||||
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
{isFilterOpen && (
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
|
<div className="lg:w-[25%] h-fit w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
|
||||||
<ReactDatePicker
|
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
|
||||||
selected={monthYearFilter}
|
<Icon icon="stash:filter-light" fontSize={30} />
|
||||||
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
Filter
|
||||||
onChange={(date) => setMonthYearFilter(date)}
|
</h2>
|
||||||
dateFormat="MM | yyyy"
|
<div className="border-t border-black my-4 dark:border-white"></div>
|
||||||
placeholderText={t("selectYear")}
|
<div className="space-y-6">
|
||||||
showMonthYearPicker
|
<div>
|
||||||
/>
|
<label
|
||||||
</div>
|
htmlFor="search"
|
||||||
|
className="block text-sm font-medium text-gray-700 dark:text-white"
|
||||||
<div>
|
>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
|
{t("search")}
|
||||||
<div className="flex flex-row justify justify-between gap-2">
|
</label>
|
||||||
<ReactDatePicker
|
<Input
|
||||||
selectsRange
|
value={searchTitle}
|
||||||
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
onChange={(e) => setSearchTitle(e.target.value)}
|
||||||
startDate={dateRange[0]}
|
onKeyUp={handleKeyUp}
|
||||||
endDate={dateRange[1]}
|
onKeyDown={handleKeyDown}
|
||||||
onChange={(update) => {
|
type="text"
|
||||||
setDateRange(update);
|
id="search"
|
||||||
}}
|
placeholder={t("searchTitle")}
|
||||||
placeholderText={t("selectDate")}
|
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
||||||
onCalendarClose={() => setCalenderState(!calenderState)}
|
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
|
<label className="block text-sm font-medium text-gray-700 dark:text-white">
|
||||||
<ul className="mt-2 space-y-2">
|
{t("monthYear")}
|
||||||
{categories.map((category: any) => (
|
</label>
|
||||||
<li key={category?.id}>
|
<ReactDatePicker
|
||||||
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
|
selected={monthYearFilter}
|
||||||
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
|
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
|
onChange={(date) => setMonthYearFilter(date)}
|
||||||
|
dateFormat="MM | yyyy"
|
||||||
|
placeholderText={t("selectYear")}
|
||||||
|
showMonthYearPicker
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-white">
|
||||||
|
{t("date")}
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-row justify justify-between gap-2">
|
||||||
|
<ReactDatePicker
|
||||||
|
selectsRange
|
||||||
|
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
||||||
|
startDate={dateRange[0]}
|
||||||
|
endDate={dateRange[1]}
|
||||||
|
onChange={(update) => {
|
||||||
|
setDateRange(update);
|
||||||
|
}}
|
||||||
|
placeholderText={t("selectDate")}
|
||||||
|
onCalendarClose={() => setCalenderState(!calenderState)}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{handleClose ? (
|
||||||
|
<Icon
|
||||||
|
icon="carbon:close-filled"
|
||||||
|
onClick={handleDeleteDate}
|
||||||
|
width="20"
|
||||||
|
inline
|
||||||
|
color="#216ba5"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||||
|
{t("categories")}
|
||||||
|
</h3>
|
||||||
|
<ul className="mt-2 space-y-2">
|
||||||
|
{categories.map((category: any) => (
|
||||||
|
<li key={category?.id}>
|
||||||
|
<label
|
||||||
|
className="inline-flex items-center"
|
||||||
|
htmlFor={`${category.id}`}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={`${category.id}`}
|
||||||
|
value={category.id}
|
||||||
|
checked={categoryFilter.includes(String(category.id))}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleCategoryFilter(Boolean(e), category.id)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="ml-2 text-gray-700 dark:text-white">
|
||||||
|
{category?.name}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{/* Garis */}
|
||||||
|
<div className="border-t border-black my-4 dark:border-white"></div>
|
||||||
|
{/* Garis */}
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||||
|
Format
|
||||||
|
</h3>
|
||||||
|
<ul className="mt-2 space-y-2">
|
||||||
|
<li>
|
||||||
|
<label className="inline-flex items-center">
|
||||||
|
<Checkbox
|
||||||
|
id="png"
|
||||||
|
value="png"
|
||||||
|
checked={formatFilter.includes("png")}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleFormatFilter(Boolean(e), "png")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="ml-2 text-gray-700 dark:text-white">
|
||||||
|
PNG
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
))}
|
<li>
|
||||||
</ul>
|
<label className="inline-flex items-center">
|
||||||
</div>
|
<Checkbox
|
||||||
{/* Garis */}
|
id="jpeg"
|
||||||
<div className="border-t border-black my-4 dark:border-white"></div>
|
value="jpeg"
|
||||||
{/* Garis */}
|
checked={formatFilter.includes("jpeg")}
|
||||||
<div>
|
onCheckedChange={(e) =>
|
||||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
|
handleFormatFilter(Boolean(e), "jpeg")
|
||||||
<ul className="mt-2 space-y-2">
|
}
|
||||||
<li>
|
/>
|
||||||
<label className="inline-flex items-center">
|
<span className="ml-2 text-gray-700 dark:text-white">
|
||||||
<Checkbox id="png" value="png" checked={formatFilter.includes("png")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "png")} />
|
JPEG
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">PNG</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label className="inline-flex items-center">
|
<label className="inline-flex items-center">
|
||||||
<Checkbox id="jpeg" value="jpeg" checked={formatFilter.includes("jpeg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpeg")} />
|
<Checkbox
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">JPEG</span>
|
id="jpg"
|
||||||
</label>
|
value="jpg"
|
||||||
</li>
|
checked={formatFilter.includes("jpg")}
|
||||||
<li>
|
onCheckedChange={(e) =>
|
||||||
<label className="inline-flex items-center">
|
handleFormatFilter(Boolean(e), "jpg")
|
||||||
<Checkbox id="jpg" value="jpg" checked={formatFilter.includes("jpg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpg")} />
|
}
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">JPG</span>
|
/>
|
||||||
</label>
|
<span className="ml-2 text-gray-700 dark:text-white">
|
||||||
</li>
|
JPG
|
||||||
</ul>
|
</span>
|
||||||
</div>
|
</label>
|
||||||
<div className="border-t border-black dark:border-white my-4"></div>
|
</li>
|
||||||
<div className="text-center">
|
</ul>
|
||||||
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
|
</div>
|
||||||
<b>Reset Filter</b>
|
<div className="border-t border-black dark:border-white my-4"></div>
|
||||||
</a>
|
<div className="text-center">
|
||||||
|
<a
|
||||||
|
onClick={cleanCheckbox}
|
||||||
|
className="text-[#bb3523] cursor-pointer"
|
||||||
|
>
|
||||||
|
<b>Reset Filter</b>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
{/* Right */}
|
{/* Right */}
|
||||||
<div className="w-full lg:w-[75%]">
|
<div className="w-full lg:w-[75%]">
|
||||||
|
|
@ -505,7 +652,11 @@ const FilterPage = () => {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex flex-col items-end mb-4">
|
<div className="flex flex-col items-end mb-4">
|
||||||
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
|
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
|
||||||
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
|
<select
|
||||||
|
defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"}
|
||||||
|
onChange={(e) => handleSorting(e)}
|
||||||
|
className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
||||||
|
>
|
||||||
<option value="latest">{t("latest")}</option>
|
<option value="latest">{t("latest")}</option>
|
||||||
<option value="popular">{t("mostPopular")}</option>
|
<option value="popular">{t("mostPopular")}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -529,24 +680,50 @@ const FilterPage = () => {
|
||||||
{imageData?.length > 0 ? (
|
{imageData?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{imageData?.map((image: any) => (
|
{imageData?.map((image: any) => (
|
||||||
<Card key={image?.id} className="hover:scale-105 transition-transform duration-300">
|
<Card
|
||||||
|
key={image?.id}
|
||||||
|
className="hover:scale-105 transition-transform duration-300"
|
||||||
|
>
|
||||||
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
||||||
<Link href={`/image/detail/${image?.slug}`}>
|
<Link href={`/image/detail/${image?.slug}`}>
|
||||||
{/* <img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" /> */}
|
{/* <img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" /> */}
|
||||||
<div className="img-container h-60 bg-[#e9e9e9] cursor-pointer rounded-lg">
|
<div className="img-container h-60 bg-[#e9e9e9] cursor-pointer rounded-lg">
|
||||||
<ImageBlurry src={image?.thumbnailLink} alt={image?.title} style={{ objectFit: "contain", width: "100%", height: "100%" }} />
|
<ImageBlurry
|
||||||
|
src={image?.thumbnailLink}
|
||||||
|
alt={image?.title}
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2 text-[10px] mx-2 mt-2">
|
<div className="flex flex-row items-center gap-2 text-[10px] mx-2 mt-2">
|
||||||
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
{formatDateToIndonesian(
|
||||||
|
new Date(image?.createdAt)
|
||||||
|
)}{" "}
|
||||||
|
{image?.timezone ? image?.timezone : "WIB"}|{" "}
|
||||||
|
<Icon
|
||||||
|
icon="formkit:eye"
|
||||||
|
width="15"
|
||||||
|
height="15"
|
||||||
|
/>
|
||||||
{image?.clickCount}{" "}
|
{image?.clickCount}{" "}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
fill="#f00"
|
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"
|
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>{" "}
|
</svg>{" "}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{image?.title}</div>
|
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">
|
||||||
|
{image?.title}
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -554,12 +731,24 @@ const FilterPage = () => {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<p className="flex items-center justify-center text-black">
|
<p className="flex items-center justify-center text-black">
|
||||||
<Image width={1920} height={1080} src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
|
<Image
|
||||||
|
width={1920}
|
||||||
|
height={1080}
|
||||||
|
src="/assets/empty-data.png"
|
||||||
|
alt="empty"
|
||||||
|
className="h-60 w-60 my-4"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{totalData > 1 && <LandingPagination table={table} totalData={totalData} totalPage={totalPage} />}
|
{totalData > 1 && (
|
||||||
|
<LandingPagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,22 @@
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/components/ui/carousel";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { deleteBlogComments, getBlogComments, getDetailIndeks, getPublicSuggestionList, postBlogComments, publicDetailBlog } from "@/service/landing/landing";
|
import {
|
||||||
|
deleteBlogComments,
|
||||||
|
getBlogComments,
|
||||||
|
getDetailIndeks,
|
||||||
|
getPublicSuggestionList,
|
||||||
|
postBlogComments,
|
||||||
|
publicDetailBlog,
|
||||||
|
} from "@/service/landing/landing";
|
||||||
import { formatDateToIndonesian } from "@/utils/globals";
|
import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
|
@ -94,7 +107,9 @@ const IndeksDetail = () => {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
async function sendCommentChild(parentId: any) {
|
async function sendCommentChild(parentId: any) {
|
||||||
const inputMsg = document.querySelector(`#input-comment-${parentId}`) as HTMLInputElement;
|
const inputMsg = document.querySelector(
|
||||||
|
`#input-comment-${parentId}`
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
if (inputMsg && inputMsg.value.length > 3) {
|
if (inputMsg && inputMsg.value.length > 3) {
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -166,32 +181,54 @@ const IndeksDetail = () => {
|
||||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
const toBase64 = (str: string) =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? Buffer.from(str).toString("base64")
|
||||||
|
: window.btoa(str);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-4 lg:px-60 lg:p-12">
|
<div className="p-4 lg:px-28 lg:p-12">
|
||||||
{/* Judul */}
|
{/* Judul */}
|
||||||
<div className="flex flex-col mb-5">
|
<div className="flex flex-col mb-5">
|
||||||
<h1 className="text-base lg:text-lg mb-2">Index / Detail</h1>
|
<h1 className="text-base lg:text-lg mb-2">Index / Detail</h1>
|
||||||
<h1 className="flex flex-row font-bold text-center text-lg lg:text-2xl">{indeksData?.title}</h1>
|
<h1 className="flex flex-row font-bold text-center text-lg lg:text-2xl">
|
||||||
|
{indeksData?.title}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
{/* Gambar Utama */}
|
{/* Gambar Utama */}
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={2560} height={1440} src={indeksData?.thumbnailLink} alt="Main" className="h-fit lg:h-[550px] w-full rounded-lg" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={2560}
|
||||||
|
height={1440}
|
||||||
|
src={indeksData?.thumbnailLink}
|
||||||
|
alt="Main"
|
||||||
|
className="w-full max-h-[550px] object-contain rounded-xl "
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer Informasi */}
|
{/* Footer Informasi */}
|
||||||
<div className="text-gray-500 flex border-t mt-4">
|
<div className="text-gray-500 flex border-t mt-4">
|
||||||
<div className="flex mt-2">
|
<div className="flex mt-2">
|
||||||
<p className="text-xs lg:text-sm mb-2 ">
|
<p className="text-xs lg:text-sm mb-2 ">
|
||||||
{t("by")} <span className="font-semibold text-gray-500">{indeksData?.uploaderName}</span> | {t("updatedOn")} {indeksData?.createdAt} WIB
|
{t("by")}
|
||||||
|
<span className="font-semibold text-gray-500">
|
||||||
|
{indeksData?.uploaderName}
|
||||||
|
</span>
|
||||||
|
| {t("updatedOn")} {indeksData?.createdAt} WIB
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Keterangan */}
|
{/* Keterangan */}
|
||||||
<div className="w-auto">
|
<div className="w-auto">
|
||||||
<p className="font-light text-base lg:text-lg text-justify" dangerouslySetInnerHTML={{ __html: indeksData?.description }} />
|
<p
|
||||||
|
className="font-light text-base lg:text-lg text-justify"
|
||||||
|
dangerouslySetInnerHTML={{ __html: indeksData?.description }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -199,9 +236,19 @@ const IndeksDetail = () => {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex flex-col py-5 p-0 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
|
<div className="flex flex-col py-5 p-0 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
|
||||||
<div className="gap-5 flex flex-col px-4 lg:px-16">
|
<div className="gap-5 flex flex-col px-4 lg:px-16">
|
||||||
<p className="flex items-start text-bases lg:text-lg">{t("comment")}</p>
|
<p className="flex items-start text-bases lg:text-lg">
|
||||||
<Textarea placeholder="Type your comments here." className="flex w-full" onChange={getInputValue} value={message} />
|
{t("comment")}
|
||||||
<button className="flex items-start bg-[#bb3523] text-white rounded-lg text-sm lg:text-base w-fit px-3 lg:px-4 py-1" onClick={() => postData()}>
|
</p>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Type your comments here."
|
||||||
|
className="flex w-full"
|
||||||
|
onChange={getInputValue}
|
||||||
|
value={message}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="flex items-start bg-[#bb3523] text-white rounded-lg text-sm lg:text-base w-fit px-3 lg:px-4 py-1"
|
||||||
|
onClick={() => postData()}
|
||||||
|
>
|
||||||
{t("send")}
|
{t("send")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -212,15 +259,40 @@ const IndeksDetail = () => {
|
||||||
{listComments?.map((data: any) => (
|
{listComments?.map((data: any) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row mt-2 px-4 lg:px-14">
|
<div className="flex flex-row mt-2 px-4 lg:px-14">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={512} height={512} className="h-10 lg:h-20 w-10 lg:w-20" src="/assets/img/user-avatar-yellow.svg" alt="#" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={512}
|
||||||
|
height={512}
|
||||||
|
className="h-10 lg:h-20 w-10 lg:w-20"
|
||||||
|
src="/assets/img/user-avatar-yellow.svg"
|
||||||
|
alt="#"
|
||||||
|
/>
|
||||||
<div className="border border-slate-300 w-full p-4 bg-white gap-1">
|
<div className="border border-slate-300 w-full p-4 bg-white gap-1">
|
||||||
<p className="flex justify-between text-sm text-slate-500 lg:text-base border-b-2 border-slate-200 mb-2">
|
<p className="flex justify-between text-sm text-slate-500 lg:text-base border-b-2 border-slate-200 mb-2">
|
||||||
<b>{Number(data.commentFrom?.roleId) == 2 || Number(data.commentFrom?.roleId) == 3 || Number(data.commentFrom?.roleId) == 4 ? "HUMAS POLRI" : data.commentFrom?.fullname}</b>
|
<b>
|
||||||
{`${new Date(data.createdAt).getDate()}/${new Date(data.createdAt).getMonth() + 1}/${new Date(data.createdAt).getFullYear()} ${new Date(data.createdAt).getHours()}:${new Date(data.createdAt).getMinutes()}`}
|
{Number(data.commentFrom?.roleId) == 2 ||
|
||||||
|
Number(data.commentFrom?.roleId) == 3 ||
|
||||||
|
Number(data.commentFrom?.roleId) == 4
|
||||||
|
? "HUMAS POLRI"
|
||||||
|
: data.commentFrom?.fullname}
|
||||||
|
</b>
|
||||||
|
{`${new Date(data.createdAt).getDate()}/${
|
||||||
|
new Date(data.createdAt).getMonth() + 1
|
||||||
|
}/${new Date(data.createdAt).getFullYear()} ${new Date(
|
||||||
|
data.createdAt
|
||||||
|
).getHours()}:${new Date(data.createdAt).getMinutes()}`}
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-500 text-sm lg:text-base mb-4">
|
||||||
|
{data.message}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500 text-sm lg:text-base mb-4">{data.message}</p>
|
|
||||||
<div className="gap-3">
|
<div className="gap-3">
|
||||||
<a href="javascript:void(0)" className="text-xs lg:text-sm mr-2 bg-blue-500 text-white py-1 px-2 hover:bg-blue-300 hover:text-black rounded-md" onClick={() => showInput(`comment-id-${data.id}`)}>
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
className="text-xs lg:text-sm mr-2 bg-blue-500 text-white py-1 px-2 hover:bg-blue-300 hover:text-black rounded-md"
|
||||||
|
onClick={() => showInput(`comment-id-${data.id}`)}
|
||||||
|
>
|
||||||
{t("reply")}
|
{t("reply")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
|
|
@ -240,9 +312,21 @@ const IndeksDetail = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row px-4 pl-[55px] lg:px-14 lg:pl-[135px] mt-2" id={`comment-id-${data.id}`}>
|
<div
|
||||||
<Input type="text" id={`input-comment-${data.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
|
className="flex flex-row px-4 pl-[55px] lg:px-14 lg:pl-[135px] mt-2"
|
||||||
<a href="javascript:void(0)" className="flex py-1 px-2 rounded-md items-center ml-2 bg-[#f7b357] text-white text-sm lg:text-base" onClick={() => postDataChild(data.id)}>
|
id={`comment-id-${data.id}`}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id={`input-comment-${data.id}`}
|
||||||
|
className="p-4 focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder={t("enterReply")}
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
className="flex py-1 px-2 rounded-md items-center ml-2 bg-[#f7b357] text-white text-sm lg:text-base"
|
||||||
|
onClick={() => postDataChild(data.id)}
|
||||||
|
>
|
||||||
{t("send")}
|
{t("send")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -250,24 +334,54 @@ const IndeksDetail = () => {
|
||||||
? data.children?.map((child1: any) => (
|
? data.children?.map((child1: any) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-12 lg:pl-32">
|
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-12 lg:pl-32">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={512} height={512} className="h-10 lg:h-20 w-10 lg:w-20" src="/assets/img/user-avatar-yellow.svg" alt="#" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={512}
|
||||||
|
height={512}
|
||||||
|
className="h-10 lg:h-20 w-10 lg:w-20"
|
||||||
|
src="/assets/img/user-avatar-yellow.svg"
|
||||||
|
alt="#"
|
||||||
|
/>
|
||||||
<div className="border border-slate-300 w-full p-4 bg-white gap-1">
|
<div className="border border-slate-300 w-full p-4 bg-white gap-1">
|
||||||
<p className="flex justify-between text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
<p className="flex justify-between text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
|
||||||
<b>{Number(child1.commentFrom?.roleId) == 2 || Number(child1.commentFrom?.roleId) == 3 || Number(child1.commentFrom?.roleId) == 4 ? "HUMAS POLRI" : child1.commentFrom?.fullname}</b>
|
<b>
|
||||||
{`${new Date(child1.createdAt).getDate()}/${new Date(child1.createdAt).getMonth() + 1}/${new Date(child1.createdAt).getFullYear()} ${new Date(child1.createdAt).getHours()}:${new Date(
|
{Number(child1.commentFrom?.roleId) == 2 ||
|
||||||
|
Number(child1.commentFrom?.roleId) == 3 ||
|
||||||
|
Number(child1.commentFrom?.roleId) == 4
|
||||||
|
? "HUMAS POLRI"
|
||||||
|
: child1.commentFrom?.fullname}
|
||||||
|
</b>
|
||||||
|
{`${new Date(child1.createdAt).getDate()}/${
|
||||||
|
new Date(child1.createdAt).getMonth() + 1
|
||||||
|
}/${new Date(
|
||||||
|
child1.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
child1.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
child1.createdAt
|
child1.createdAt
|
||||||
).getMinutes()}`}
|
).getMinutes()}`}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500 text-sm mb-4">{child1.message}</p>
|
<p className="text-slate-500 text-sm mb-4">
|
||||||
|
{child1.message}
|
||||||
|
</p>
|
||||||
<div className="gap-3">
|
<div className="gap-3">
|
||||||
<a href="javascript:void(0)" className="mr-2 text-xs lg:text-sm bg-blue-500 text-white py-1 px-2 hover:bg-blue-300 hover:text-black rounded-md" onClick={() => showInput(`comment-id-${child1.id}`)}>
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
className="mr-2 text-xs lg:text-sm bg-blue-500 text-white py-1 px-2 hover:bg-blue-300 hover:text-black rounded-md"
|
||||||
|
onClick={() =>
|
||||||
|
showInput(`comment-id-${child1.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
{t("reply")}
|
{t("reply")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="javascript:void(0)"
|
href="javascript:void(0)"
|
||||||
className="text-xs lg:text-sm bg-red-500 text-white py-1 px-2 hover:bg-red-300 hover:text-black rounded-md"
|
className="text-xs lg:text-sm bg-red-500 text-white py-1 px-2 hover:bg-red-300 hover:text-black rounded-md"
|
||||||
style={
|
style={
|
||||||
Number(child1.commentFrom?.id) == Number(userId)
|
Number(child1.commentFrom?.id) ==
|
||||||
|
Number(userId)
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
display: "none",
|
display: "none",
|
||||||
|
|
@ -280,9 +394,21 @@ const IndeksDetail = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row justify-center px-4 pl-[87px] lg:px-14 lg:pl-[205px] mt-2" id={`comment-id-${child1.id}`}>
|
<div
|
||||||
<Input type="text" id={`input-comment-${child1.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
|
className="flex flex-row justify-center px-4 pl-[87px] lg:px-14 lg:pl-[205px] mt-2"
|
||||||
<a href="javascript:void(0)" className="flex text-sm lg:text-base py-1 px-2 rounded-md items-center ml-2 bg-[#f7b357] text-white" onClick={() => postDataChild(child1.id)}>
|
id={`comment-id-${child1.id}`}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id={`input-comment-${child1.id}`}
|
||||||
|
className="p-4 focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder={t("enterReply")}
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
className="flex text-sm lg:text-base py-1 px-2 rounded-md items-center ml-2 bg-[#f7b357] text-white"
|
||||||
|
onClick={() => postDataChild(child1.id)}
|
||||||
|
>
|
||||||
{t("send")}
|
{t("send")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -290,24 +416,59 @@ const IndeksDetail = () => {
|
||||||
? child1.children?.map((child2: any) => (
|
? child1.children?.map((child2: any) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-20 lg:pl-48">
|
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-20 lg:pl-48">
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={512} height={512} className="h-10 lg:h-20 w-10 lg:w-20" src="/assets/img/user-avatar-yellow.svg" alt="#" />
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={512}
|
||||||
|
height={512}
|
||||||
|
className="h-10 lg:h-20 w-10 lg:w-20"
|
||||||
|
src="/assets/img/user-avatar-yellow.svg"
|
||||||
|
alt="#"
|
||||||
|
/>
|
||||||
<div className="border border-slate-300 w-full p-4 bg-white gap-2">
|
<div className="border border-slate-300 w-full p-4 bg-white gap-2">
|
||||||
<p className="flex justify-between text-slate-500 text-xs lg:text-base border-b-2 border-slate-200 mb-2">
|
<p className="flex justify-between text-slate-500 text-xs lg:text-base border-b-2 border-slate-200 mb-2">
|
||||||
<b>{Number(child2.commentFrom?.roleId) == 2 || Number(child2.commentFrom?.roleId) == 3 || Number(child2.commentFrom?.roleId) == 4 ? "HUMAS POLRI" : child2.commentFrom?.fullname}</b>
|
<b>
|
||||||
{`${new Date(child2.createdAt).getDate()}/${new Date(child2.createdAt).getMonth() + 1}/${new Date(child2.createdAt).getFullYear()} ${new Date(child2.createdAt).getHours()}:${new Date(
|
{Number(child2.commentFrom?.roleId) ==
|
||||||
|
2 ||
|
||||||
|
Number(child2.commentFrom?.roleId) ==
|
||||||
|
3 ||
|
||||||
|
Number(child2.commentFrom?.roleId) == 4
|
||||||
|
? "HUMAS POLRI"
|
||||||
|
: child2.commentFrom?.fullname}
|
||||||
|
</b>
|
||||||
|
{`${new Date(
|
||||||
|
child2.createdAt
|
||||||
|
).getDate()}/${
|
||||||
|
new Date(child2.createdAt).getMonth() +
|
||||||
|
1
|
||||||
|
}/${new Date(
|
||||||
|
child2.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
child2.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
child2.createdAt
|
child2.createdAt
|
||||||
).getMinutes()}`}
|
).getMinutes()}`}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500 text-sm mb-4">{child2.message}</p>
|
<p className="text-slate-500 text-sm mb-4">
|
||||||
|
{child2.message}
|
||||||
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<a href="javascript:void(0)" className="mr-2 text-xs lg:text-sm bg-blue-500 text-white py-1 px-2 hover:bg-blue-300 hover:text-black rounded-md" onClick={() => showInput("comment-id-" + child2.id)}>
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
className="mr-2 text-xs lg:text-sm bg-blue-500 text-white py-1 px-2 hover:bg-blue-300 hover:text-black rounded-md"
|
||||||
|
onClick={() =>
|
||||||
|
showInput("comment-id-" + child2.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
{t("reply")}
|
{t("reply")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="javascript:void(0)"
|
href="javascript:void(0)"
|
||||||
className="text-xs lg:text-sm bg-red-500 text-white py-1 px-2 hover:bg-red-300 hover:text-black rounded-md"
|
className="text-xs lg:text-sm bg-red-500 text-white py-1 px-2 hover:bg-red-300 hover:text-black rounded-md"
|
||||||
style={
|
style={
|
||||||
Number(child2.commentFrom?.id) == Number(userId)
|
Number(child2.commentFrom?.id) ==
|
||||||
|
Number(userId)
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
display: "none",
|
display: "none",
|
||||||
|
|
@ -320,9 +481,21 @@ const IndeksDetail = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row px-4 pl-[120px] lg:px-14 lg:pl-[270px] mt-2" id={`comment-id-${child2.id}`}>
|
<div
|
||||||
<Input type="text" id={`comment-id-${child2.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
|
className="flex flex-row px-4 pl-[120px] lg:px-14 lg:pl-[270px] mt-2"
|
||||||
<a href="javascript:void(0)" className="flex text-sm lg:text-base py-1 px-2 rounded-md items-center ml-2 bg-[#f7b357] text-white" onClick={() => postDataChild(child1.id)}>
|
id={`comment-id-${child2.id}`}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id={`comment-id-${child2.id}`}
|
||||||
|
className="p-4 focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder={t("enterReply")}
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
className="flex text-sm lg:text-base py-1 px-2 rounded-md items-center ml-2 bg-[#f7b357] text-white"
|
||||||
|
onClick={() => postDataChild(child1.id)}
|
||||||
|
>
|
||||||
{t("send")}
|
{t("send")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -339,14 +512,25 @@ const IndeksDetail = () => {
|
||||||
|
|
||||||
{/* Konten Serupa */}
|
{/* Konten Serupa */}
|
||||||
<div className="space-x-5 flex flex-col px-4 lg:px-16 py-16 gap-5">
|
<div className="space-x-5 flex flex-col px-4 lg:px-16 py-16 gap-5">
|
||||||
<h1 className="font-bold text-base lg:text-xl px-4 lg:px-8"> {t("relatedPosts")}</h1>
|
<h1 className="font-bold text-base lg:text-xl px-4 lg:px-8">
|
||||||
|
{" "}
|
||||||
|
{t("relatedPosts")}
|
||||||
|
</h1>
|
||||||
<Carousel>
|
<Carousel>
|
||||||
<CarouselContent className="w-full max-w-7xl">
|
<CarouselContent className="w-full max-w-7xl">
|
||||||
{indexData?.map((relate: any) => (
|
{indexData?.map((relate: any) => (
|
||||||
<CarouselItem key={relate?.id} className="md:basis-1/2 lg:basis-1/3">
|
<CarouselItem
|
||||||
<Link href={`/indeks/detail/${relate?.slug}`} className="relative group overflow-hidden shadow-md hover:shadow-lg">
|
key={relate?.id}
|
||||||
|
className="md:basis-1/2 lg:basis-1/3"
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={`/indeks/detail/${relate?.slug}`}
|
||||||
|
className="relative group overflow-hidden shadow-md hover:shadow-lg"
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
alt=""
|
alt=""
|
||||||
width={2560}
|
width={2560}
|
||||||
height={1440}
|
height={1440}
|
||||||
|
|
@ -354,10 +538,17 @@ const IndeksDetail = () => {
|
||||||
className="w-full rounded-lg h-40 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300"
|
className="w-full rounded-lg h-40 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300"
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-0 left-0 right-0 bg-gray-600 border-l-4 border-[#bb3523] rounded-lg backdrop-blur-sm text-white p-2">
|
<div className="absolute bottom-0 left-0 right-0 bg-gray-600 border-l-4 border-[#bb3523] rounded-lg backdrop-blur-sm text-white p-2">
|
||||||
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{relate?.categoryName}</span>
|
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">
|
||||||
<h1 className="text-sm lg:text-lg mb-2 font-semibold h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{relate?.title}</h1>
|
{relate?.categoryName}
|
||||||
|
</span>
|
||||||
|
<h1 className="text-sm lg:text-lg mb-2 font-semibold h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
|
||||||
|
{relate?.title}
|
||||||
|
</h1>
|
||||||
<p className="flex flex-row items-center text-[10px] gap-2">
|
<p className="flex flex-row items-center text-[10px] gap-2">
|
||||||
{formatDateToIndonesian(new Date(relate?.createdAt))} {relate?.timezone ? relate?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> {relate.clickCount}{" "}
|
{formatDateToIndonesian(new Date(relate?.createdAt))}{" "}
|
||||||
|
{relate?.timezone ? relate?.timezone : "WIB"} |{" "}
|
||||||
|
<Icon icon="formkit:eye" width="15" height="15" />{" "}
|
||||||
|
{relate.clickCount}{" "}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
import { getIndeksData } from "@/service/landing/landing";
|
import { getIndeksData } from "@/service/landing/landing";
|
||||||
import { formatDateToIndonesian } from "@/utils/globals";
|
import { formatDateToIndonesian, htmlToString } from "@/utils/globals";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
@ -53,9 +53,11 @@ const Indeks: React.FC = () => {
|
||||||
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
|
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
|
||||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
const toBase64 = (str: string) =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? Buffer.from(str).toString("base64")
|
||||||
|
: window.btoa(str);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-4 lg:px-14">
|
<div className="px-4 lg:px-14">
|
||||||
|
|
@ -70,22 +72,44 @@ const Indeks: React.FC = () => {
|
||||||
{indeksData?.map(
|
{indeksData?.map(
|
||||||
(indeks: any, index: number) =>
|
(indeks: any, index: number) =>
|
||||||
index == count && (
|
index == count && (
|
||||||
<div key={indeks?.id} className="relative h-[310px] lg:h-[435px]">
|
<div
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={2560} height={1440} src={indeks?.thumbnailLink} alt="image" className="w-full h-[310px] lg:h-[435px] rounded-lg object-cover" />
|
key={indeks?.id}
|
||||||
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-white p-4 rounded-b-lg">
|
className="relative h-[310px] lg:h-[435px]"
|
||||||
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-xs px-2 py-1">{indeks?.categoryName}</span>
|
>
|
||||||
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={2560}
|
||||||
|
height={1440}
|
||||||
|
src={indeks?.thumbnailLink}
|
||||||
|
alt="image"
|
||||||
|
className="w-full h-[310px] lg:h-[435px] rounded-lg object-cover"
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 bg-black/15 backdrop-brightness-50 text-white p-4 rounded-b-lg">
|
||||||
|
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-xs px-2 py-1">
|
||||||
|
{indeks?.categoryName}
|
||||||
|
</span>
|
||||||
<Link href={`/indeks/detail/${indeks?.slug}`}>
|
<Link href={`/indeks/detail/${indeks?.slug}`}>
|
||||||
<h2 className="text-2xl font-bold mt-2">{indeks?.title}</h2>
|
<h2 className="text-2xl font-bold mt-2">
|
||||||
|
{indeks?.title}
|
||||||
|
</h2>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-xs flex flex-row items-center gap-1 mt-1">
|
<p className="text-xs flex flex-row items-center gap-1 mt-1">
|
||||||
{formatDateToIndonesian(new Date(indeks?.createdAt))} {indeks?.timezone ? indeks?.timezone : "WIB"} |{" "}
|
{formatDateToIndonesian(new Date(indeks?.createdAt))}{" "}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
|
{indeks?.timezone ? indeks?.timezone : "WIB"} |{" "}
|
||||||
|
{/* <svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
/>
|
/>
|
||||||
</svg>{" "}
|
</svg>{" "} */}
|
||||||
{indeks?.clickCount}
|
{/* {indeks?.clickCount} */}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -108,22 +132,49 @@ const Indeks: React.FC = () => {
|
||||||
{indeksData?.map(
|
{indeksData?.map(
|
||||||
(indeksRight: any, index: number) =>
|
(indeksRight: any, index: number) =>
|
||||||
(index == count + 1 || index == count + 2) && (
|
(index == count + 1 || index == count + 2) && (
|
||||||
<div key={indeksRight?.id} className="relative h-[310px] lg:h-[215px]">
|
<div
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1920} height={1080} src={indeksRight?.thumbnailLink} alt="image" className="w-full h-[310px] lg:h-[215px] rounded-lg " />
|
key={indeksRight?.id}
|
||||||
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-white p-4 rounded-b-lg">
|
className="relative h-[310px] lg:h-[215px]"
|
||||||
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-xs px-2 py-1">{indeksRight?.categoryName}</span>
|
>
|
||||||
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={1920}
|
||||||
|
height={1080}
|
||||||
|
src={indeksRight?.thumbnailLink}
|
||||||
|
alt="image"
|
||||||
|
className="w-full h-[310px] lg:h-[215px] rounded-lg "
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 bg-black/15 backdrop-brightness-50 text-white p-4 rounded-b-lg">
|
||||||
|
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-xs px-2 py-1">
|
||||||
|
{indeksRight?.categoryName}
|
||||||
|
</span>
|
||||||
<Link href={`/indeks/detail/${indeksRight?.slug}`}>
|
<Link href={`/indeks/detail/${indeksRight?.slug}`}>
|
||||||
<h2 className="text-xl font-bold mt-2">{indeksRight?.title}</h2>
|
<h2 className="text-xl font-bold mt-2">
|
||||||
|
{indeksRight?.title}
|
||||||
|
</h2>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-xs flex flex-row items-center gap-1 mt-1 ml-2">
|
<p className="text-xs flex flex-row items-center gap-1 mt-1 ml-2">
|
||||||
{formatDateToIndonesian(new Date(indeksRight?.createdAt))} {indeksRight?.timezone ? indeksRight?.timezone : "WIB"}|{" "}
|
{formatDateToIndonesian(
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
|
new Date(indeksRight?.createdAt)
|
||||||
|
)}{" "}
|
||||||
|
{indeksRight?.timezone
|
||||||
|
? indeksRight?.timezone
|
||||||
|
: "WIB"}
|
||||||
|
|{" "}
|
||||||
|
{/* <svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
/>
|
/>
|
||||||
</svg>{" "}
|
</svg>{" "}
|
||||||
{indeksRight?.clickCount}
|
{indeksRight?.clickCount} */}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -165,14 +216,31 @@ const Indeks: React.FC = () => {
|
||||||
{indeksData?.map(
|
{indeksData?.map(
|
||||||
(indeksBottom: any, index: number) =>
|
(indeksBottom: any, index: number) =>
|
||||||
index < 3 && (
|
index < 3 && (
|
||||||
<div key={indeksBottom?.id} className="flex flex-col md:flex-row items-start p-4 gap-4">
|
<div
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={2560} height={1440} src={indeksBottom?.thumbnailLink} alt="" className="h-40 object-cover rounded-lg w-full lg:w-full lg:h-[300px]" />
|
key={indeksBottom?.id}
|
||||||
|
className="flex flex-col md:flex-row items-start p-4 gap-4"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={500}
|
||||||
|
height={250}
|
||||||
|
src={indeksBottom?.thumbnailLink}
|
||||||
|
alt=""
|
||||||
|
className="h-40 object-cover rounded-lg w-full lg:w-[550px] lg:h-[200px]"
|
||||||
|
/>
|
||||||
<div className="flex flex-col justify-between w-full">
|
<div className="flex flex-col justify-between w-full">
|
||||||
<p className="text-sm">{indeksBottom?.date}</p>
|
<p className="text-sm">{indeksBottom?.date}</p>
|
||||||
<Link href={`/indeks/detail/${indeksBottom?.slug}`} className="text-2xl font-semibold text-gray-800 dark:text-white">
|
<Link
|
||||||
|
href={`/indeks/detail/${indeksBottom?.slug}`}
|
||||||
|
className="text-2xl font-semibold text-gray-800 dark:text-white"
|
||||||
|
>
|
||||||
{indeksBottom?.title}
|
{indeksBottom?.title}
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-sm text-gray-600 dark:text-white mt-2">{indeksBottom?.description}</p>
|
<p className="text-sm text-gray-600 dark:text-white mt-2">
|
||||||
|
{htmlToString(indeksBottom?.description)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,50 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
import {
|
||||||
import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverArrow,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
import { CalendarIcon } from "lucide-react";
|
import { CalendarIcon } from "lucide-react";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { detailSchedule, listSchedule, listScheduleNextPublic, listSchedulePrevPublic, listScheduleTodayPublic, searchSchedules } from "@/service/schedule/schedule";
|
import {
|
||||||
|
detailSchedule,
|
||||||
|
listSchedule,
|
||||||
|
listScheduleNextPublic,
|
||||||
|
listSchedulePrevPublic,
|
||||||
|
listScheduleTodayPublic,
|
||||||
|
searchSchedules,
|
||||||
|
} from "@/service/schedule/schedule";
|
||||||
import { usePathname, useRouter } from "@/i18n/routing";
|
import { usePathname, useRouter } from "@/i18n/routing";
|
||||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
import {
|
||||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -292,13 +324,21 @@ const Schedule = (props: any) => {
|
||||||
|
|
||||||
function getLastWeek(today: Date | undefined) {
|
function getLastWeek(today: Date | undefined) {
|
||||||
if (today) {
|
if (today) {
|
||||||
return new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
|
return new Date(
|
||||||
|
today.getFullYear(),
|
||||||
|
today.getMonth(),
|
||||||
|
today.getDate() - 7
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextWeek(today: Date | undefined) {
|
function getNextWeek(today: Date | undefined) {
|
||||||
if (today) {
|
if (today) {
|
||||||
return new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
|
return new Date(
|
||||||
|
today.getFullYear(),
|
||||||
|
today.getMonth(),
|
||||||
|
today.getDate() + 7
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,13 +463,22 @@ const Schedule = (props: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
function setItemSchedule(id: string, date: string) {
|
function setItemSchedule(id: string, date: string) {
|
||||||
const itemFound: any = schedules?.filter((s: any) => s.dateInRange.includes(date) && s.timeIndex.split(",").includes(id));
|
const itemFound: any = schedules?.filter(
|
||||||
|
(s: any) =>
|
||||||
|
s.dateInRange.includes(date) && s.timeIndex.split(",").includes(id)
|
||||||
|
);
|
||||||
|
|
||||||
if (itemFound?.length > 0) {
|
if (itemFound?.length > 0) {
|
||||||
if (itemFound?.length == 1) {
|
if (itemFound?.length == 1) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={`cursor-pointer text-center ${Number(itemFound[0]?.uploaderLevelNumber) == 1 ? "bg-yellow-300" : Number(itemFound[0]?.uploaderLevelNumber) == 2 ? "bg-blue-400" : "bg-gray-500"}`}
|
className={`cursor-pointer text-center ${
|
||||||
|
Number(itemFound[0]?.uploaderLevelNumber) == 1
|
||||||
|
? "bg-yellow-300"
|
||||||
|
: Number(itemFound[0]?.uploaderLevelNumber) == 2
|
||||||
|
? "bg-blue-400"
|
||||||
|
: "bg-gray-500"
|
||||||
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
getItem(itemFound[0]);
|
getItem(itemFound[0]);
|
||||||
}}
|
}}
|
||||||
|
|
@ -452,7 +501,9 @@ const Schedule = (props: any) => {
|
||||||
<b>{`${itemFound?.length} Jadwal Bersamaan`}</b>
|
<b>{`${itemFound?.length} Jadwal Bersamaan`}</b>
|
||||||
</p>
|
</p>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger className="font-bold text-blue-300">Lihat Jadwal</DropdownMenuTrigger>
|
<DropdownMenuTrigger className="font-bold text-blue-300">
|
||||||
|
Lihat Jadwal
|
||||||
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
{itemFound?.map((list: any) => (
|
{itemFound?.map((list: any) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
|
|
@ -471,7 +522,10 @@ const Schedule = (props: any) => {
|
||||||
))}
|
))}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<div className="border-0 dropdown-menu schedule-list" aria-labelledby="view-schedule"></div>
|
<div
|
||||||
|
className="border-0 dropdown-menu schedule-list"
|
||||||
|
aria-labelledby="view-schedule"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -480,73 +534,121 @@ const Schedule = (props: any) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Awal Komponen Kiri */}
|
{/* Awal Komponen Kiri */}
|
||||||
<div className="relative px-4 lg:px-10 lg:py-10 py-4 bg-[#f7f7f7] dark:bg-slate-800">
|
<div className="relative pl-4 lg:px-8 lg:py-10 py-4 bg-[#f7f7f7] dark:bg-slate-800">
|
||||||
<Popover>
|
<div className="flex flex-row items-center">
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button variant={"outline"} className={cn("w-[240px] py-4 justify-start text-left font-normal", !startDate && "text-muted-foreground")}>
|
|
||||||
<CalendarIcon />
|
|
||||||
{startDate ? format(startDate, "MMM yyyy") : <span>Pick a date</span>}
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={startDate}
|
|
||||||
onSelect={(e) => {
|
|
||||||
handleChangeDate(e);
|
|
||||||
}}
|
|
||||||
initialFocus
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
<div className="container relative py-4">
|
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<a className="text-black dark:text-white flex flex-row w-fit gap-2 py-4 items-center cursor-pointer">
|
<Button
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
variant={"outline"}
|
||||||
<path fill="currentColor" d="M20 3H4a1 1 0 0 0-1 1v2.227l.008.223a3 3 0 0 0 .772 1.795L8 12.886V21a1 1 0 0 0 1.316.949l6-2l.108-.043A1 1 0 0 0 16 19v-6.586l4.121-4.12A3 3 0 0 0 21 6.171V4a1 1 0 0 0-1-1" />
|
className={cn(
|
||||||
</svg>
|
"w-[240px] py-3 justify-start text-left font-normal",
|
||||||
Filter
|
!startDate && "text-muted-foreground"
|
||||||
<svg className="flex items-center justify-center" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
)}
|
||||||
<path fill="currentColor" fill-rule="evenodd" d="m6 7l6 6l6-6l2 2l-8 8l-8-8z" />
|
>
|
||||||
</svg>
|
<CalendarIcon />
|
||||||
</a>
|
{startDate ? (
|
||||||
|
format(startDate, "MMM yyyy")
|
||||||
|
) : (
|
||||||
|
<span>Pick a date</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent align="start" className="flex p-0 rounded-md w-fit">
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
<div className="flex flex-col items-center justify-between gap-1.5 p-2 border-b text-default-600 rounded-none">
|
<Calendar
|
||||||
<div className="gap-6 flex flex-row justify-end">
|
mode="single"
|
||||||
<p className="font-semibold">Filter</p>
|
selected={startDate}
|
||||||
<button className="text-blue-400" onClick={doFilter}>
|
onSelect={(e) => {
|
||||||
{t("save")}
|
handleChangeDate(e);
|
||||||
</button>
|
}}
|
||||||
</div>
|
initialFocus
|
||||||
<div className="border w-full border-t border-slate-500"></div>
|
/>
|
||||||
<div className="overflow-y-auto flex flex-col gap-2 h-[200px] ">
|
|
||||||
<p className="text-center font-semibold">Region Filter</p>
|
|
||||||
{city?.map((list) => (
|
|
||||||
<div className="mt-2 gap-2 flex flex-row">
|
|
||||||
{" "}
|
|
||||||
<input type="checkbox" className="" id={`filterCategory-${list.key}`} value={list.id} checked={regionFilter?.includes(list.id)} onChange={handleRegionFilter} />
|
|
||||||
<p>{list?.name}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
<div className="flex flex-col lg:flex-row gap-3">
|
|
||||||
{regionName?.map((list: any) => (
|
<div className="container relative py-4 flex flex-row items-center gap-4">
|
||||||
<div className="text-left">
|
<Popover>
|
||||||
<button onClick={() => deleteFilterhandler(list.id)} key={list.key} id={list.id} className="text-black bg-yellow-300 w-fit p-3 flex justify-center items-center rounded-lg">
|
<PopoverTrigger asChild>
|
||||||
{list.name}
|
<a className="text-black dark:text-white flex flex-row w-fit gap-3 py-2 px-3 items-center cursor-pointer border border-black rounded-md ">
|
||||||
<Icon icon="icon-park-outline:delete-two" className="items-center" />
|
<svg
|
||||||
</button>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</div>
|
width="1em"
|
||||||
))}
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M20 3H4a1 1 0 0 0-1 1v2.227l.008.223a3 3 0 0 0 .772 1.795L8 12.886V21a1 1 0 0 0 1.316.949l6-2l.108-.043A1 1 0 0 0 16 19v-6.586l4.121-4.12A3 3 0 0 0 21 6.171V4a1 1 0 0 0-1-1"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Filter
|
||||||
|
<svg
|
||||||
|
className="flex items-center justify-center"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="m6 7l6 6l6-6l2 2l-8 8l-8-8z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
align="start"
|
||||||
|
className="flex p-0 rounded-md w-fit"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center justify-between gap-1.5 p-2 border-b text-default-600 rounded-none">
|
||||||
|
<div className="gap-6 flex flex-row justify-end">
|
||||||
|
<p className="font-semibold">Filter</p>
|
||||||
|
<button className="text-blue-400" onClick={doFilter}>
|
||||||
|
{t("save")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="border w-full border-t border-slate-500"></div>
|
||||||
|
<div className="overflow-y-auto flex flex-col gap-2 h-[200px] ">
|
||||||
|
<p className="text-center font-semibold">Region Filter</p>
|
||||||
|
{city?.map((list) => (
|
||||||
|
<div className="mt-2 gap-2 flex flex-row">
|
||||||
|
{" "}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className=""
|
||||||
|
id={`filterCategory-${list.key}`}
|
||||||
|
value={list.id}
|
||||||
|
checked={regionFilter?.includes(list.id)}
|
||||||
|
onChange={handleRegionFilter}
|
||||||
|
/>
|
||||||
|
<p>{list?.name}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<div className="flex flex-col lg:flex-row gap-3">
|
||||||
|
{regionName?.map((list: any) => (
|
||||||
|
<div className="text-left">
|
||||||
|
<button
|
||||||
|
onClick={() => deleteFilterhandler(list.id)}
|
||||||
|
key={list.key}
|
||||||
|
id={list.id}
|
||||||
|
className="text-black bg-yellow-300 w-fit p-3 flex justify-center items-center rounded-lg"
|
||||||
|
>
|
||||||
|
{list.name}
|
||||||
|
<Icon
|
||||||
|
icon="icon-park-outline:delete-two"
|
||||||
|
className="items-center"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col lg:flex-row gap-6">
|
<div className="flex flex-col lg:flex-row gap-1">
|
||||||
<div className="h-[500px] overflow-y-auto md:overflow-y-auto w-full lg:w-3/4 ">
|
<div className="h-[500px] overflow-y-auto md:overflow-y-auto w-full lg:w-3/4 ">
|
||||||
<div className="container-fluid relative">
|
<div className="container-fluid relative">
|
||||||
<div className="grid grid-cols-1 mt-8">
|
<div className="grid grid-cols-1 mt-8">
|
||||||
|
|
@ -554,53 +656,125 @@ const Schedule = (props: any) => {
|
||||||
<table className="w-full text-sm text-start">
|
<table className="w-full text-sm text-start">
|
||||||
<thead className="text-md">
|
<thead className="text-md">
|
||||||
<tr className="h-full">
|
<tr className="h-full">
|
||||||
<th className="text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[120px]">{t("timeTable")}</th>
|
<th className="text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[210px]">
|
||||||
|
{t("timeTable")}
|
||||||
|
</th>
|
||||||
<th className="text-center border border-r-0 border-gray-100 dark:border-gray-700 py-6 w-[20px]">
|
<th className="text-center border border-r-0 border-gray-100 dark:border-gray-700 py-6 w-[20px]">
|
||||||
<a onClick={() => changePrevWeek()} className="cursor-pointer h-fit self-center bottom-0">
|
<a
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24">
|
onClick={() => changePrevWeek()}
|
||||||
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
|
className="cursor-pointer h-fit self-center bottom-0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="40px"
|
||||||
|
height="40px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th className={`text-center cursor-pointer border border-l-0 h-full border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[0] ? "bg-red-600 text-white" : ""}`}>
|
<th
|
||||||
|
className={`text-center cursor-pointer border border-l-0 h-full border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[0]
|
||||||
|
? "bg-red-600 text-white"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{/* <a className="cursor-pointer h-fit self-center bottom-0" >
|
{/* <a className="cursor-pointer h-fit self-center bottom-0" >
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
|
||||||
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
|
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>{" "} */}
|
</a>{" "} */}
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<p className="text-2xl">{dateAWeek[0]?.split("-")[2]}</p>
|
<p className="text-2xl">
|
||||||
|
{dateAWeek[0]?.split("-")[2]}
|
||||||
|
</p>
|
||||||
<p>{t("monday")}</p>
|
<p>{t("monday")}</p>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[1] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
|
<th
|
||||||
<div className="text-2xl">{dateAWeek[1]?.split("-")[2]}</div>
|
className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[1]
|
||||||
|
? "bg-[#BE0106] text-white rounded-lg"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="text-2xl">
|
||||||
|
{dateAWeek[1]?.split("-")[2]}
|
||||||
|
</div>
|
||||||
{t("tuesday")}
|
{t("tuesday")}
|
||||||
</th>
|
</th>
|
||||||
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[2] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
|
<th
|
||||||
<div className="text-2xl">{dateAWeek[2]?.split("-")[2]}</div>
|
className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[2]
|
||||||
|
? "bg-[#BE0106] text-white rounded-lg"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="text-2xl">
|
||||||
|
{dateAWeek[2]?.split("-")[2]}
|
||||||
|
</div>
|
||||||
{t("wednesday")}
|
{t("wednesday")}
|
||||||
</th>
|
</th>
|
||||||
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[3] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
|
<th
|
||||||
<div className="text-2xl">{dateAWeek[3]?.split("-")[2]}</div>
|
className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[3]
|
||||||
|
? "bg-[#BE0106] text-white rounded-lg"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="text-2xl">
|
||||||
|
{dateAWeek[3]?.split("-")[2]}
|
||||||
|
</div>
|
||||||
{t("thursday")}
|
{t("thursday")}
|
||||||
</th>
|
</th>
|
||||||
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[4] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
|
<th
|
||||||
<div className="text-2xl">{dateAWeek[4]?.split("-")[2]}</div>
|
className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[4]
|
||||||
|
? "bg-[#BE0106] text-white rounded-lg"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="text-2xl">
|
||||||
|
{dateAWeek[4]?.split("-")[2]}
|
||||||
|
</div>
|
||||||
{t("friday")}
|
{t("friday")}
|
||||||
</th>
|
</th>
|
||||||
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[5] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
|
<th
|
||||||
<div className="text-2xl">{dateAWeek[5]?.split("-")[2]}</div>
|
className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[5]
|
||||||
|
? "bg-[#BE0106] text-white rounded-lg"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="text-2xl">
|
||||||
|
{dateAWeek[5]?.split("-")[2]}
|
||||||
|
</div>
|
||||||
{t("saturday")}
|
{t("saturday")}
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
onClick={() => changeNextWeek()}
|
onClick={() => changeNextWeek()}
|
||||||
className={`text-center border cursor-pointer border-r-0 border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
className={`text-center border cursor-pointer border-r-0 border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
|
||||||
new Date().toISOString().slice(0, 10) == dateAWeek[6] ? "bg-[#BE0106] text-white rounded-lg" : ""
|
new Date().toISOString().slice(0, 10) ==
|
||||||
|
dateAWeek[6]
|
||||||
|
? "bg-[#BE0106] text-white rounded-lg"
|
||||||
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<p className="text-2xl">{dateAWeek[6]?.split("-")[2]}</p>
|
<p className="text-2xl">
|
||||||
|
{dateAWeek[6]?.split("-")[2]}
|
||||||
|
</p>
|
||||||
<p>{t("sunday")}</p>
|
<p>{t("sunday")}</p>
|
||||||
</div>
|
</div>
|
||||||
{/* <a className="cursor-pointer h-fit p-0 m-0 self-center">
|
{/* <a className="cursor-pointer h-fit p-0 m-0 self-center">
|
||||||
|
|
@ -610,9 +784,20 @@ const Schedule = (props: any) => {
|
||||||
</a> */}
|
</a> */}
|
||||||
</th>
|
</th>
|
||||||
<th className="text-center border-l-0 border border-gray-100 dark:border-gray-700 py-6 w-[20px]">
|
<th className="text-center border-l-0 border border-gray-100 dark:border-gray-700 py-6 w-[20px]">
|
||||||
<a onClick={() => changeNextWeek()} className="cursor-pointer">
|
<a
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24">
|
onClick={() => changeNextWeek()}
|
||||||
<path fill="currentColor" d="m11.71 15.29l2.59-2.59a.996.996 0 0 0 0-1.41L11.71 8.7c-.63-.62-1.71-.18-1.71.71v5.17c0 .9 1.08 1.34 1.71.71" />
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="40px"
|
||||||
|
height="40px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m11.71 15.29l2.59-2.59a.996.996 0 0 0 0-1.41L11.71 8.7c-.63-.62-1.71-.18-1.71.71v5.17c0 .9 1.08 1.34 1.71.71"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
|
|
@ -621,16 +806,34 @@ const Schedule = (props: any) => {
|
||||||
<tbody>
|
<tbody>
|
||||||
{timeList.map((times) => (
|
{timeList.map((times) => (
|
||||||
<tr key={times.id}>
|
<tr key={times.id}>
|
||||||
<th className="text-center border border-gray-100 dark:border-gray-700 py-5">{times.time}</th>
|
<th className="text-center border border-gray-100 dark:border-gray-700 py-5">
|
||||||
<td colSpan={2} className="p-3 border border-gray-100 dark:border-gray-700 ">
|
{times.time}
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
colSpan={2}
|
||||||
|
className="p-3 border border-gray-100 dark:border-gray-700 "
|
||||||
|
>
|
||||||
{setItemSchedule(times.id, dateList[0])}
|
{setItemSchedule(times.id, dateList[0])}
|
||||||
</td>
|
</td>
|
||||||
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[1])}</td>
|
<td className="border border-gray-100 dark:border-gray-700">
|
||||||
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[2])}</td>
|
{setItemSchedule(times.id, dateList[1])}
|
||||||
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[3])}</td>
|
</td>
|
||||||
<td className="p-3 border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[4])}</td>
|
<td className="border border-gray-100 dark:border-gray-700">
|
||||||
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[5])}</td>
|
{setItemSchedule(times.id, dateList[2])}
|
||||||
<td colSpan={2} className="border border-gray-100 dark:border-gray-700">
|
</td>
|
||||||
|
<td className="border border-gray-100 dark:border-gray-700">
|
||||||
|
{setItemSchedule(times.id, dateList[3])}
|
||||||
|
</td>
|
||||||
|
<td className="p-3 border border-gray-100 dark:border-gray-700">
|
||||||
|
{setItemSchedule(times.id, dateList[4])}
|
||||||
|
</td>
|
||||||
|
<td className="border border-gray-100 dark:border-gray-700">
|
||||||
|
{setItemSchedule(times.id, dateList[5])}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
colSpan={2}
|
||||||
|
className="border border-gray-100 dark:border-gray-700"
|
||||||
|
>
|
||||||
{setItemSchedule(times.id, dateList[6])}
|
{setItemSchedule(times.id, dateList[6])}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -654,10 +857,18 @@ const Schedule = (props: any) => {
|
||||||
className="pl-8 pr-4 py-1 w-full border rounded-full text-sm focus:outline-none"
|
className="pl-8 pr-4 py-1 w-full border rounded-full text-sm focus:outline-none"
|
||||||
/>
|
/>
|
||||||
<span className="absolute left-2 top-1/2 transform -translate-y-1/2">
|
<span className="absolute left-2 top-1/2 transform -translate-y-1/2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
||||||
<path fill="currentColor" d="M10.5 2a8.5 8.5 0 1 0 5.262 15.176l3.652 3.652a1 1 0 0 0 1.414-1.414l-3.652-3.652A8.5 8.5 0 0 0 10.5 2M4 10.5a6.5 6.5 0 1 1 13 0a6.5 6.5 0 0 1-13 0" />
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M10.5 2a8.5 8.5 0 1 0 5.262 15.176l3.652 3.652a1 1 0 0 0 1.414-1.414l-3.652-3.652A8.5 8.5 0 0 0 10.5 2M4 10.5a6.5 6.5 0 1 1 13 0a6.5 6.5 0 0 1-13 0"
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -700,8 +911,13 @@ const Schedule = (props: any) => {
|
||||||
<AccordionItem value="item-1">
|
<AccordionItem value="item-1">
|
||||||
<AccordionTrigger>{t("todaySchedule")}</AccordionTrigger>
|
<AccordionTrigger>{t("todaySchedule")}</AccordionTrigger>
|
||||||
{todayList?.map((list: any) => (
|
{todayList?.map((list: any) => (
|
||||||
<AccordionContent key={list?.id} className="flex flex-row gap-3">
|
<AccordionContent
|
||||||
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
|
key={list?.id}
|
||||||
|
className="flex flex-row gap-3"
|
||||||
|
>
|
||||||
|
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">
|
||||||
|
{new Date(list.startDate).getDate()}
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="font-bold">{list?.title}</h3>
|
<h3 className="font-bold">{list?.title}</h3>
|
||||||
<p className="flex flex-row items-center gap-2">
|
<p className="flex flex-row items-center gap-2">
|
||||||
|
|
@ -725,8 +941,13 @@ const Schedule = (props: any) => {
|
||||||
<AccordionItem value="item-2">
|
<AccordionItem value="item-2">
|
||||||
<AccordionTrigger>{t("previousSchedule")}</AccordionTrigger>
|
<AccordionTrigger>{t("previousSchedule")}</AccordionTrigger>
|
||||||
{prevdayList?.map((list: any) => (
|
{prevdayList?.map((list: any) => (
|
||||||
<AccordionContent key={list?.id} className="flex flex-row gap-3">
|
<AccordionContent
|
||||||
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
|
key={list?.id}
|
||||||
|
className="flex flex-row gap-3"
|
||||||
|
>
|
||||||
|
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">
|
||||||
|
{new Date(list.startDate).getDate()}
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="font-bold">{list?.title}</h3>
|
<h3 className="font-bold">{list?.title}</h3>
|
||||||
<p className="flex flex-row items-center gap-2">
|
<p className="flex flex-row items-center gap-2">
|
||||||
|
|
@ -750,8 +971,13 @@ const Schedule = (props: any) => {
|
||||||
<AccordionItem value="item-3">
|
<AccordionItem value="item-3">
|
||||||
<AccordionTrigger>{t("nextSchedule")}</AccordionTrigger>
|
<AccordionTrigger>{t("nextSchedule")}</AccordionTrigger>
|
||||||
{nextdayList?.map((list: any) => (
|
{nextdayList?.map((list: any) => (
|
||||||
<AccordionContent key={list?.id} className="flex flex-row gap-3">
|
<AccordionContent
|
||||||
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
|
key={list?.id}
|
||||||
|
className="flex flex-row gap-3"
|
||||||
|
>
|
||||||
|
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">
|
||||||
|
{new Date(list.startDate).getDate()}
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="font-bold">{list?.title}</h3>
|
<h3 className="font-bold">{list?.title}</h3>
|
||||||
<p className="flex flex-row items-center gap-2">
|
<p className="flex flex-row items-center gap-2">
|
||||||
|
|
@ -846,13 +1072,17 @@ const Schedule = (props: any) => {
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>
|
<AlertDialogTitle>
|
||||||
<h1 className="my-4 font-light">JADWAL / {detail?.isYoutube == true ? "LIVE STREAMING" : "DETAIL"}</h1>
|
<h1 className="my-4 font-light">
|
||||||
|
JADWAL /{" "}
|
||||||
|
{detail?.isYoutube == true ? "LIVE STREAMING" : "DETAIL"}
|
||||||
|
</h1>
|
||||||
<p className="font-bold">{detail?.title}</p>
|
<p className="font-bold">{detail?.title}</p>
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
<p className="flex flex-row items-center gap-2">
|
<p className="flex flex-row items-center gap-2">
|
||||||
<Icon icon="iconamoon:clock-thin" />
|
<Icon icon="iconamoon:clock-thin" />
|
||||||
{detail?.date} {detail?.startTime} - {detail?.endTime} {detail?.timezone ? detail.timezone : "WIB"}
|
{detail?.date} {detail?.startTime} - {detail?.endTime}{" "}
|
||||||
|
{detail?.timezone ? detail.timezone : "WIB"}
|
||||||
</p>
|
</p>
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,23 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
||||||
<div className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600 relative z-[1] bg-default-50">
|
<div className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600 relative z-[1] bg-default-50">
|
||||||
<div className="max-w-[520px] pt-16 ps-20 ">
|
<div className="max-w-[520px] pt-16 ps-20 ">
|
||||||
<Link href="/" className="mb-6 inline-block">
|
<Link href="/" className="mb-6 inline-block">
|
||||||
<Image src="/assets/mediahub-logo.png" alt="" width={250} height={250} className="mb-10 w-full h-full" />
|
<Image
|
||||||
|
src="/assets/mediahub-logo.png"
|
||||||
|
alt=""
|
||||||
|
width={250}
|
||||||
|
height={250}
|
||||||
|
className="mb-10 w-full h-full"
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute left-0 2xl:bottom-[-160px] bottom-[-130px] h-full w-full z-[-1]">
|
<div className="absolute left-0 2xl:bottom-[-160px] bottom-[-130px] h-full w-full z-[-1]">
|
||||||
<Image src="/assets/vector-login.svg" alt="" width={300} height={300} className="mb-10 w-full h-full" />
|
<Image
|
||||||
|
src="/assets/vector-login.svg"
|
||||||
|
alt=""
|
||||||
|
width={300}
|
||||||
|
height={300}
|
||||||
|
className="mb-10 w-full h-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 relative">
|
<div className="flex-1 relative">
|
||||||
|
|
@ -40,12 +52,12 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
||||||
<Logo />
|
<Logo />
|
||||||
</Link>
|
</Link>
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="text-left 2xl:mb-10 mb-4 mt-10">
|
{/* <div className="text-left 2xl:mb-10 mb-4 mt-10">
|
||||||
<h4 className="font-semibold text-3xl text-left">{t("logInPlease")}</h4>
|
<h4 className="font-semibold text-3xl text-left">{t("logInPlease")}</h4>
|
||||||
<div className="text-default-500 text-base">
|
<div className="text-default-500 text-base">
|
||||||
{t("acc")} <span className="text-red-500">{t("reg")}</span>
|
{t("acc")} <span className="text-red-500">{t("reg")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
{/* <div className="relative border-b-[#9AA2AF] border-opacity-[16%] border-b pt-6">
|
{/* <div className="relative border-b-[#9AA2AF] border-opacity-[16%] border-b pt-6">
|
||||||
<div className="absolute inline-block bg-default-50 dark:bg-default-100 left-1/2 top-1/2 transform -translate-x-1/2 px-4 min-w-max text-sm text-default-500 font-normal">
|
<div className="absolute inline-block bg-default-50 dark:bg-default-100 left-1/2 top-1/2 transform -translate-x-1/2 px-4 min-w-max text-sm text-default-500 font-normal">
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
SelectItem,
|
SelectItem,
|
||||||
} from "@radix-ui/react-select";
|
} from "@radix-ui/react-select";
|
||||||
import { SelectGroup } from "@/components/ui/select";
|
import { SelectGroup } from "@/components/ui/select";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -43,6 +44,13 @@ interface Option {
|
||||||
userLevelId: string;
|
userLevelId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormCollaboration() {
|
export default function FormCollaboration() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -257,12 +265,7 @@ export default function FormCollaboration() {
|
||||||
control={control}
|
control={control}
|
||||||
name="naration"
|
name="naration"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
ref={editor}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className="dark:text-black"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors.naration?.message && (
|
{errors.naration?.message && (
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
SelectItem,
|
SelectItem,
|
||||||
} from "@radix-ui/react-select";
|
} from "@radix-ui/react-select";
|
||||||
import { SelectGroup } from "@/components/ui/select";
|
import { SelectGroup } from "@/components/ui/select";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -43,6 +44,13 @@ interface Option {
|
||||||
userLevelId: string;
|
userLevelId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormInternal() {
|
export default function FormInternal() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -253,12 +261,7 @@ export default function FormInternal() {
|
||||||
control={control}
|
control={control}
|
||||||
name="naration"
|
name="naration"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
ref={editor}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className="dark:text-black"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors.naration?.message && (
|
{errors.naration?.message && (
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ import { error } from "@/lib/swal";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import WavesurferPlayer from "@wavesurfer/react";
|
import WavesurferPlayer from "@wavesurfer/react";
|
||||||
import WaveSurfer from "wavesurfer.js";
|
import WaveSurfer from "wavesurfer.js";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -135,6 +136,7 @@ export default function FormAudioDetail() {
|
||||||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
|
|
@ -452,11 +454,11 @@ export default function FormAudioDetail() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Audio</p>
|
<p className="text-lg font-semibold mb-3">{t("form-audio")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -477,8 +479,8 @@ export default function FormAudioDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={detail?.category.name} // Nilai default berdasarkan detail
|
value={detail?.category.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -500,8 +502,8 @@ export default function FormAudioDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -516,7 +518,7 @@ export default function FormAudioDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Label className="text-xl text-black">File Mediaaa</Label>
|
<Label className="text-xl space-y-2">{t("file-media")}</Label>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className={"container example"}>
|
<div className={"container example"}>
|
||||||
{detailThumb?.map((url: any, index: number) => (
|
{detailThumb?.map((url: any, index: number) => (
|
||||||
|
|
@ -561,7 +563,7 @@ export default function FormAudioDetail() {
|
||||||
<Card className=" h-[600px]">
|
<Card className=" h-[600px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -585,7 +587,7 @@ export default function FormAudioDetail() {
|
||||||
|
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{detail?.tags
|
{detail?.tags
|
||||||
?.split(",")
|
?.split(",")
|
||||||
|
|
@ -601,8 +603,8 @@ export default function FormAudioDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="5"
|
id="5"
|
||||||
|
|
@ -639,10 +641,10 @@ export default function FormAudioDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")}(0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 border mx-3">
|
<div className="px-3 py-3 border mx-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||||
</div>
|
</div>
|
||||||
{/* {detail?.isPublish == false ? (
|
{/* {detail?.isPublish == false ? (
|
||||||
|
|
@ -662,14 +664,15 @@ export default function FormAudioDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:check" className="mr-3" /> Setujui
|
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("3")}
|
onClick={() => actionApproval("3")}
|
||||||
className="bg-orange-400 hover:bg-orange-300"
|
className="bg-orange-400 hover:bg-orange-300"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:comment-o" className="mr-3" /> Revisi
|
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||||
|
{t("revision")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("4")}
|
onClick={() => actionApproval("4")}
|
||||||
|
|
@ -677,7 +680,7 @@ export default function FormAudioDetail() {
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:times" className="mr-3" />
|
<Icon icon="fa:times" className="mr-3" />
|
||||||
Tolak
|
{t("reject")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -688,7 +691,7 @@ export default function FormAudioDetail() {
|
||||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||||
<DialogContent size="md">
|
<DialogContent size="md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>{t("leave-comment")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file, index) => (
|
? files?.map((file, index) => (
|
||||||
|
|
@ -725,7 +728,7 @@ export default function FormAudioDetail() {
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
>
|
>
|
||||||
Semua
|
{t("all")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -870,7 +873,7 @@ export default function FormAudioDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -879,7 +882,7 @@ export default function FormAudioDetail() {
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ import { Item } from "@radix-ui/react-dropdown-menu";
|
||||||
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 { useTranslations } from "next-intl";
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -88,6 +89,7 @@ export default function FormAudio() {
|
||||||
const scheduleType = Cookies.get("scheduleType");
|
const scheduleType = Cookies.get("scheduleType");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||||
const [tags, setTags] = useState<any[]>([]);
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
|
|
@ -708,11 +710,11 @@ export default function FormAudio() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Audio</p>
|
<p className="text-lg font-semibold mb-3">{t("form-audio")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -732,8 +734,8 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -758,7 +760,7 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3 py-2">
|
<div className="flex flex-row items-center gap-3 py-2">
|
||||||
<Label>Bantuan AI</Label>
|
<Label>{t("ai-assistance")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch
|
<Switch
|
||||||
defaultChecked={isSwitchOn}
|
defaultChecked={isSwitchOn}
|
||||||
|
|
@ -774,7 +776,7 @@ export default function FormAudio() {
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Bahasa</Label>
|
<Label>{t("language")}</Label>
|
||||||
<Select onValueChange={setSelectedLanguage}>
|
<Select onValueChange={setSelectedLanguage}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -786,7 +788,7 @@ export default function FormAudio() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Writing Style</Label>
|
<Label>{t("writing-style")}</Label>
|
||||||
<Select onValueChange={setSelectedWritingStyle}>
|
<Select onValueChange={setSelectedWritingStyle}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -805,7 +807,7 @@ export default function FormAudio() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Article Size</Label>
|
<Label>{t("article-size")}</Label>
|
||||||
<Select onValueChange={setSelectedSize}>
|
<Select onValueChange={setSelectedSize}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -826,7 +828,7 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Main Keyword</Label>
|
<Label>{t("main-keyword")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -850,7 +852,7 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -872,7 +874,7 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>SEO</Label>
|
<Label>{t("seo")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -883,15 +885,9 @@ export default function FormAudio() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-semibold">
|
<p className="font-semibold">
|
||||||
Kata kunci untuk disertakan dalam teks
|
{t("Keywords to include in the text")}
|
||||||
</p>
|
|
||||||
<p className="text-sm">
|
|
||||||
JIka Anda tidak Memberikan kata kunci, kami akan secara
|
|
||||||
otomatis membuat kata kunci yang relevan dari kata kunci
|
|
||||||
utama untuk setiap bagian dan menggunakannya untuk membuat
|
|
||||||
artikel. Untuk menambahkan kata kunci baru, ketik ',
|
|
||||||
+ kata kunci'.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm">{t("title-key")}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Textarea
|
<Textarea
|
||||||
value={selectedSEO}
|
value={selectedSEO}
|
||||||
|
|
@ -901,7 +897,7 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Instruksi Khusus (Optional)</Label>
|
<Label>{t("special-instructions")} (Optional)</Label>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -959,7 +955,7 @@ export default function FormAudio() {
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
Edit
|
{t("update")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
@ -968,8 +964,8 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="">
|
<div className="space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -992,8 +988,8 @@ export default function FormAudio() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -1006,11 +1002,10 @@ export default function FormAudio() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan mp3 atau wav Ukuran maksimal
|
{t("upload-file-audio-max")}
|
||||||
100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1028,7 +1023,7 @@ export default function FormAudio() {
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={handleRemoveAllFiles}
|
onClick={handleRemoveAllFiles}
|
||||||
>
|
>
|
||||||
Remove All
|
{t("remove-all")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
@ -1042,7 +1037,7 @@ export default function FormAudio() {
|
||||||
<Card className=" h-[500px]">
|
<Card className=" h-[500px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -1063,8 +1058,8 @@ export default function FormAudio() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3 space-y-2">
|
||||||
<Label htmlFor="tags">Tags</Label>
|
<Label htmlFor="tags">{t("tags")}</Label>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -1092,8 +1087,8 @@ export default function FormAudio() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -1116,12 +1111,12 @@ export default function FormAudio() {
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const audioSchema = z.object({
|
const audioSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -87,6 +89,13 @@ type Option = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormAudioUpdate() {
|
export default function FormAudioUpdate() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -108,6 +117,7 @@ export default function FormAudioUpdate() {
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
const scheduleType = Cookies.get("scheduleType");
|
const scheduleType = Cookies.get("scheduleType");
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||||
const [tags, setTags] = useState<any[]>([]);
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
|
|
@ -590,11 +600,11 @@ export default function FormAudioUpdate() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Audio</p>
|
<p className="text-lg font-semibold mb-3">{t("form-audio")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -615,8 +625,8 @@ export default function FormAudioUpdate() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={detail?.categoryId.name} // Nilai default berdasarkan detail
|
defaultValue={detail?.categoryId.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -638,17 +648,15 @@ export default function FormAudioUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor
|
||||||
ref={editor}
|
|
||||||
value={detail?.description}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
initialData={detail?.description || value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -658,8 +666,8 @@ export default function FormAudioUpdate() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -672,11 +680,10 @@ export default function FormAudioUpdate() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan format .jpg, .jpeg, atau .png.
|
{t("upload-file-audio-max")}
|
||||||
Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -685,7 +692,7 @@ export default function FormAudioUpdate() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -701,7 +708,10 @@ export default function FormAudioUpdate() {
|
||||||
) : null}
|
) : null}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Label className="text-lg font-semibold">File</Label>
|
<Label className="text-lg font-semibold">
|
||||||
|
{" "}
|
||||||
|
{t("file-media")}
|
||||||
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any) => (
|
{files.map((file: any) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -722,7 +732,7 @@ export default function FormAudioUpdate() {
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-blue-500 text-sm"
|
className="text-blue-500 text-sm"
|
||||||
>
|
>
|
||||||
Lihat File
|
{t("view-file")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -740,7 +750,7 @@ export default function FormAudioUpdate() {
|
||||||
}
|
}
|
||||||
className="form-checkbox"
|
className="form-checkbox"
|
||||||
/>
|
/>
|
||||||
<span>Semua</span>
|
<span>{t("all")}</span>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -812,7 +822,7 @@ export default function FormAudioUpdate() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -835,7 +845,7 @@ export default function FormAudioUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="tags"
|
id="tags"
|
||||||
|
|
@ -869,8 +879,8 @@ export default function FormAudioUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option: Option) => (
|
{options.map((option: Option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -890,22 +900,22 @@ export default function FormAudioUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
{/* <p>{detail?.status}</p> */}
|
{/* <p>{detail?.status}</p> */}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useRouter } from "@/i18n/routing";
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -119,6 +120,7 @@ export default function FormImageDetail() {
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
type ImageSchema = z.infer<typeof imageSchema>;
|
type ImageSchema = z.infer<typeof imageSchema>;
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
|
|
@ -418,11 +420,11 @@ export default function FormImageDetail() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
|
<p className="text-lg font-semibold mb-3">{t("form-image")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")} </Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -443,8 +445,8 @@ export default function FormImageDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={detail?.category.name} // Nilai default berdasarkan detail
|
value={detail?.category.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -466,8 +468,8 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -481,46 +483,47 @@ export default function FormImageDetail() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
<Label className="text-xl text-black">File Media</Label>
|
<Label className="text-xl ">{t("file-media")}</Label>
|
||||||
<div className="w-full ">
|
<div className="w-full ">
|
||||||
<Swiper
|
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
|
||||||
modules={[FreeMode, Navigation, Thumbs]}
|
|
||||||
navigation={false}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{detailThumb?.map((data: any) => (
|
|
||||||
<SwiperSlide key={data.id}>
|
|
||||||
<img
|
|
||||||
className="object-fill h-full w-full rounded-md"
|
|
||||||
src={data}
|
|
||||||
alt={` ${data.id}`}
|
|
||||||
/>
|
|
||||||
</SwiperSlide>
|
|
||||||
))}
|
|
||||||
</Swiper>
|
|
||||||
<div className=" mt-2 ">
|
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
slidesPerView={6}
|
modules={[FreeMode, Navigation, Thumbs]}
|
||||||
spaceBetween={8}
|
navigation={false}
|
||||||
pagination={{
|
className="w-full"
|
||||||
clickable: true,
|
|
||||||
}}
|
|
||||||
modules={[Pagination, Thumbs]}
|
|
||||||
// className="mySwiper2"
|
|
||||||
>
|
>
|
||||||
{detailThumb?.map((data: any) => (
|
{detailThumb?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide key={data.id}>
|
||||||
<img
|
<img
|
||||||
className="object-cover h-[60px] w-[80px]"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data}
|
src={data}
|
||||||
alt={` ${data.id}`}
|
alt={` ${data.id}`}
|
||||||
/>
|
/>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
<div className=" mt-2 ">
|
||||||
|
<Swiper
|
||||||
|
onSwiper={setThumbsSwiper}
|
||||||
|
slidesPerView={6}
|
||||||
|
spaceBetween={8}
|
||||||
|
pagination={{
|
||||||
|
clickable: true,
|
||||||
|
}}
|
||||||
|
modules={[Pagination, Thumbs]}
|
||||||
|
// className="mySwiper2"
|
||||||
|
>
|
||||||
|
{detailThumb?.map((data: any) => (
|
||||||
|
<SwiperSlide key={data.id}>
|
||||||
|
<img
|
||||||
|
className="object-cover h-[60px] w-[80px]"
|
||||||
|
src={data}
|
||||||
|
alt={` ${data.id}`}
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -530,7 +533,7 @@ export default function FormImageDetail() {
|
||||||
<Card className=" h-[1050px]">
|
<Card className=" h-[1050px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -551,8 +554,8 @@ export default function FormImageDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 px-3">
|
<div className="mt-3 px-3 space-y-2">
|
||||||
<Label>Pratinjau Gambar Utama</Label>
|
<Label>{t("preview")}</Label>
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
<img
|
<img
|
||||||
src={detail.thumbnailLink}
|
src={detail.thumbnailLink}
|
||||||
|
|
@ -563,7 +566,7 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{detail?.tags
|
{detail?.tags
|
||||||
?.split(",")
|
?.split(",")
|
||||||
|
|
@ -579,8 +582,8 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="5"
|
id="5"
|
||||||
|
|
@ -617,10 +620,10 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 border mx-3">
|
<div className="px-3 py-3 border mx-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||||
</div>
|
</div>
|
||||||
{/* {detail?.isPublish == false ? (
|
{/* {detail?.isPublish == false ? (
|
||||||
|
|
@ -672,14 +675,16 @@ export default function FormImageDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:check" className="mr-3" /> Setujui
|
<Icon icon="fa:check" className="mr-3" />
|
||||||
|
{t("accept")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("3")}
|
onClick={() => actionApproval("3")}
|
||||||
className="bg-orange-400 hover:bg-orange-300"
|
className="bg-orange-400 hover:bg-orange-300"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:comment-o" className="mr-3" /> Revisi
|
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||||
|
{t("revision")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("4")}
|
onClick={() => actionApproval("4")}
|
||||||
|
|
@ -687,7 +692,7 @@ export default function FormImageDetail() {
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:times" className="mr-3" />
|
<Icon icon="fa:times" className="mr-3" />
|
||||||
Tolak
|
{t("reject")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -698,7 +703,7 @@ export default function FormImageDetail() {
|
||||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||||
<DialogContent size="md">
|
<DialogContent size="md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>{t("leave-comment")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file, index) => (
|
? files?.map((file, index) => (
|
||||||
|
|
@ -735,7 +740,7 @@ export default function FormImageDetail() {
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
>
|
>
|
||||||
Semua
|
{t("all")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -880,7 +885,7 @@ export default function FormImageDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -889,7 +894,7 @@ export default function FormImageDetail() {
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ 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";
|
import { request } from "http";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -85,6 +86,7 @@ export default function FormImage() {
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
type ImageSchema = z.infer<typeof imageSchema>;
|
type ImageSchema = z.infer<typeof imageSchema>;
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
|
|
@ -714,11 +716,11 @@ export default function FormImage() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
|
<p className="text-lg font-semibold mb-3">{t("form-image")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -738,8 +740,8 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 space-y-2 w-full">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -763,8 +765,8 @@ export default function FormImage() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3 py-2">
|
<div className="flex flex-row items-center gap-3 py-3 ">
|
||||||
<Label>Bantuan AI</Label>
|
<Label>{t("ai-assistance")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch
|
<Switch
|
||||||
defaultChecked={isSwitchOn}
|
defaultChecked={isSwitchOn}
|
||||||
|
|
@ -780,7 +782,7 @@ export default function FormImage() {
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Bahasa</Label>
|
<Label>{t("language")}</Label>
|
||||||
<Select onValueChange={setSelectedLanguage}>
|
<Select onValueChange={setSelectedLanguage}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -792,7 +794,7 @@ export default function FormImage() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Writing Style</Label>
|
<Label>{t("writing-style")}</Label>
|
||||||
<Select onValueChange={setSelectedWritingStyle}>
|
<Select onValueChange={setSelectedWritingStyle}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -811,7 +813,7 @@ export default function FormImage() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Article Size</Label>
|
<Label>{t("article-size")}</Label>
|
||||||
<Select onValueChange={setSelectedSize}>
|
<Select onValueChange={setSelectedSize}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -832,7 +834,7 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Main Keyword</Label>
|
<Label>{t("main-keyword")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -856,7 +858,7 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -878,7 +880,7 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>SEO</Label>
|
<Label>{t("seo")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -889,15 +891,9 @@ export default function FormImage() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-semibold">
|
<p className="font-semibold">
|
||||||
Kata kunci untuk disertakan dalam teks
|
{t("Keywords to include in the text")}
|
||||||
</p>
|
|
||||||
<p className="text-sm">
|
|
||||||
JIka Anda tidak Memberikan kata kunci, kami akan secara
|
|
||||||
otomatis membuat kata kunci yang relevan dari kata kunci
|
|
||||||
utama untuk setiap bagian dan menggunakannya untuk membuat
|
|
||||||
artikel. Untuk menambahkan kata kunci baru, ketik ',
|
|
||||||
+ kata kunci'.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm">{t("title-key")}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Textarea
|
<Textarea
|
||||||
value={selectedSEO}
|
value={selectedSEO}
|
||||||
|
|
@ -907,7 +903,7 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Instruksi Khusus (Optional)</Label>
|
<Label>{t("special-instructions")}(Optional)</Label>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -964,7 +960,7 @@ export default function FormImage() {
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
Edit
|
{t("update")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
@ -973,8 +969,8 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -997,8 +993,8 @@ export default function FormImage() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -1011,11 +1007,10 @@ export default function FormImage() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan format .jpg, .jpeg, atau .png.
|
{t("upload-file-max")}
|
||||||
Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1024,7 +1019,7 @@ export default function FormImage() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1033,7 +1028,7 @@ export default function FormImage() {
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={handleRemoveAllFiles}
|
onClick={handleRemoveAllFiles}
|
||||||
>
|
>
|
||||||
Remove All
|
{t("remove-all")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
@ -1049,7 +1044,7 @@ export default function FormImage() {
|
||||||
<Card className=" h-[500px]">
|
<Card className=" h-[500px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -1070,9 +1065,8 @@ export default function FormImage() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3 space-y-2">
|
||||||
<Label htmlFor="tags">Tags</Label>
|
<Label htmlFor="tags">{t("tags")}</Label>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="tags"
|
id="tags"
|
||||||
|
|
@ -1099,8 +1093,8 @@ export default function FormImage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -1123,13 +1117,15 @@ export default function FormImage() {
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Link href={"/contributor/content/image"}>
|
||||||
Cancel
|
<Button type="submit" color="primary" variant="outline">
|
||||||
</Button>
|
{t("cancel")}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ import Image from "next/image";
|
||||||
import { error, loading } from "@/lib/swal";
|
import { error, loading } from "@/lib/swal";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -114,7 +115,7 @@ export default function FormImageUpdate() {
|
||||||
let uploadPersen = 0;
|
let uploadPersen = 0;
|
||||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||||
const [counterProgress, setCounterProgress] = useState(0);
|
const [counterProgress, setCounterProgress] = useState(0);
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
|
|
@ -628,11 +629,11 @@ export default function FormImageUpdate() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
|
<p className="text-lg font-semibold mb-3">{t("form-image")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -653,8 +654,8 @@ export default function FormImageUpdate() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={detail?.category.id} // Gunakan ID sebagai defaultValue
|
defaultValue={detail?.category.id} // Gunakan ID sebagai defaultValue
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -679,8 +680,8 @@ export default function FormImageUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -697,8 +698,8 @@ export default function FormImageUpdate() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -711,11 +712,10 @@ export default function FormImageUpdate() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan format .jpg, .jpeg, atau .png.
|
{t("upload-file-max")}
|
||||||
Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -724,7 +724,7 @@ export default function FormImageUpdate() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -740,7 +740,10 @@ export default function FormImageUpdate() {
|
||||||
) : null}
|
) : null}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Label className="text-lg font-semibold">File</Label>
|
<Label className="text-lg font-semibold">
|
||||||
|
{" "}
|
||||||
|
{t("file-media")}
|
||||||
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any) => (
|
{files.map((file: any) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -761,7 +764,7 @@ export default function FormImageUpdate() {
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-blue-500 text-sm"
|
className="text-blue-500 text-sm"
|
||||||
>
|
>
|
||||||
Lihat File
|
{t("view-file")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -779,7 +782,7 @@ export default function FormImageUpdate() {
|
||||||
}
|
}
|
||||||
className="form-checkbox"
|
className="form-checkbox"
|
||||||
/>
|
/>
|
||||||
<span>Semua</span>
|
<span>{t("all")}</span>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -851,7 +854,7 @@ export default function FormImageUpdate() {
|
||||||
<Card className="h-[900px] md:h-[1100px] lg:h-[800px]">
|
<Card className="h-[900px] md:h-[1100px] lg:h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -872,8 +875,8 @@ export default function FormImageUpdate() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 px-3">
|
<div className="mt-3 px-3 space-y-2">
|
||||||
<Label>Pratinjau Gambar Utama</Label>
|
<Label>{t("preview")}</Label>
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
<img
|
<img
|
||||||
src={detail.thumbnailLink}
|
src={detail.thumbnailLink}
|
||||||
|
|
@ -884,7 +887,7 @@ export default function FormImageUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="tags"
|
id="tags"
|
||||||
|
|
@ -928,8 +931,8 @@ export default function FormImageUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option: Option) => (
|
{options.map((option: Option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -949,22 +952,22 @@ export default function FormImageUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
{/* <p>{detail?.status}</p> */}
|
{/* <p>{detail?.status}</p> */}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
|
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -144,6 +145,7 @@ export default function FormConvertSPIT() {
|
||||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [detailData, setDetailData] = useState<any>(null);
|
const [detailData, setDetailData] = useState<any>(null);
|
||||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||||
|
|
@ -616,11 +618,11 @@ export default function FormConvertSPIT() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
|
<p className="text-lg font-semibold mb-3">{t("form-spit")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="contentTitle"
|
name="contentTitle"
|
||||||
|
|
@ -641,8 +643,8 @@ export default function FormConvertSPIT() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={detail?.content?.name}
|
defaultValue={detail?.content?.name}
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -678,8 +680,8 @@ export default function FormConvertSPIT() {
|
||||||
Select Original File
|
Select Original File
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3 ">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="contentDescription"
|
name="contentDescription"
|
||||||
|
|
@ -733,11 +735,11 @@ export default function FormConvertSPIT() {
|
||||||
<div className="flex items-center space-x-2 mt-3">
|
<div className="flex items-center space-x-2 mt-3">
|
||||||
<RadioGroupItem value="rewrite" id="rewrite-file" />
|
<RadioGroupItem value="rewrite" id="rewrite-file" />
|
||||||
<Label htmlFor="rewrite-file">
|
<Label htmlFor="rewrite-file">
|
||||||
Select File Hasil Rewrite
|
Select File Rewrite
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3 ">
|
<div className="py-3 space-y-2">
|
||||||
<Label>File hasil Rewrite</Label>
|
<Label>{t("file-rewrite")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="contentRewriteDescription"
|
name="contentRewriteDescription"
|
||||||
|
|
@ -766,8 +768,8 @@ export default function FormConvertSPIT() {
|
||||||
)}
|
)}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label className="text-xl">File Media</Label>
|
<Label className="text-xl">{t("file-media")}</Label>
|
||||||
<div className="w-full ">
|
<div className="w-full ">
|
||||||
<Swiper
|
<Swiper
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
|
|
@ -810,10 +812,10 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Label className="text-xl">Penempatan file</Label>
|
<Label className="text-xl">{t("file-placement")}</Label>
|
||||||
</div>
|
</div>
|
||||||
{files?.length > 1 && (
|
{files?.length > 1 && (
|
||||||
<div className="flex flex-wrap gap-2 mt-2">
|
<div className="flex flex-wrap gap-2 mt-2 justify-end mr-24 pr-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="all-content"
|
id="all-content"
|
||||||
|
|
@ -825,7 +827,7 @@ export default function FormConvertSPIT() {
|
||||||
htmlFor="all-content"
|
htmlFor="all-content"
|
||||||
className="text-xs font-medium"
|
className="text-xs font-medium"
|
||||||
>
|
>
|
||||||
All
|
{t("all")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -839,7 +841,7 @@ export default function FormConvertSPIT() {
|
||||||
htmlFor="all-nasional"
|
htmlFor="all-nasional"
|
||||||
className="text-xs font-medium"
|
className="text-xs font-medium"
|
||||||
>
|
>
|
||||||
All Nasional
|
{t("all")} Nasional
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -853,7 +855,7 @@ export default function FormConvertSPIT() {
|
||||||
htmlFor="all-wilayah"
|
htmlFor="all-wilayah"
|
||||||
className="text-xs font-medium"
|
className="text-xs font-medium"
|
||||||
>
|
>
|
||||||
All Wilayah
|
{t("all")} Wilayah
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -867,7 +869,7 @@ export default function FormConvertSPIT() {
|
||||||
htmlFor="all-international"
|
htmlFor="all-international"
|
||||||
className="text-xs font-medium"
|
className="text-xs font-medium"
|
||||||
>
|
>
|
||||||
All Internasional
|
{t("all")} Internasional
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -879,20 +881,12 @@ export default function FormConvertSPIT() {
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={file.contentFile}
|
src={file.contentFile}
|
||||||
className="w-[150px] rounded-md"
|
className="w-[180px] rounded-md"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full pl-4">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
{file.fileName}
|
{file.fileName}
|
||||||
{/* <a
|
|
||||||
onClick={() =>
|
|
||||||
handleDeleteFileApproval(file.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
|
||||||
</a> */}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -907,7 +901,7 @@ export default function FormConvertSPIT() {
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
>
|
>
|
||||||
Semua
|
{t("all")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -969,7 +963,7 @@ export default function FormConvertSPIT() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="contentCreator"
|
name="contentCreator"
|
||||||
|
|
@ -977,7 +971,7 @@ export default function FormConvertSPIT() {
|
||||||
<Input
|
<Input
|
||||||
size="md"
|
size="md"
|
||||||
type="text"
|
type="text"
|
||||||
defaultValue={detail?.contentCreator}
|
value={detail?.contentCreator}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
placeholder="Enter Title"
|
placeholder="Enter Title"
|
||||||
/>
|
/>
|
||||||
|
|
@ -991,7 +985,7 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 px-3">
|
<div className="mt-3 px-3">
|
||||||
<Label>Pratinjau Gambar Utama</Label>
|
<Label>{t("preview")}</Label>
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
<img
|
<img
|
||||||
src={detail.contentThumbnail}
|
src={detail.contentThumbnail}
|
||||||
|
|
@ -1002,7 +996,7 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{detail?.contentTag
|
{detail?.contentTag
|
||||||
?.split(",")
|
?.split(",")
|
||||||
|
|
@ -1018,8 +1012,8 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -1040,17 +1034,17 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
{/* <p>{detail?.status}</p> */}
|
{/* <p>{detail?.status}</p> */}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
|
|
@ -1059,7 +1053,7 @@ export default function FormConvertSPIT() {
|
||||||
className="bg-red-500 hover:bg-red-700"
|
className="bg-red-500 hover:bg-red-700"
|
||||||
onClick={() => deleteSpitContent()}
|
onClick={() => deleteSpitContent()}
|
||||||
>
|
>
|
||||||
Delete
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -133,6 +134,7 @@ export default function FormTeksDetail() {
|
||||||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
|
|
@ -421,13 +423,11 @@ export default function FormTeksDetail() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">
|
<p className="text-lg font-semibold mb-3">{t("form-text")}</p>
|
||||||
Form detail Konten Teks
|
|
||||||
</p>
|
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -448,8 +448,8 @@ export default function FormTeksDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={detail?.category.name} // Nilai default berdasarkan detail
|
value={detail?.category.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -471,8 +471,8 @@ export default function FormTeksDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -486,81 +486,85 @@ export default function FormTeksDetail() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Label className="text-xl text-black">File Media</Label>
|
<div className="space-y-2">
|
||||||
<div className="w-full">
|
<Label className="text-xl">{t("file-media")} </Label>
|
||||||
<Swiper
|
<div className="w-full">
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
|
||||||
modules={[FreeMode, Navigation, Thumbs]}
|
|
||||||
navigation={false}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{detailThumb?.map((data: any, index: number) => (
|
|
||||||
<SwiperSlide key={index}>
|
|
||||||
{[".jpg", ".jpeg", ".png", ".webp"].includes(
|
|
||||||
data.format
|
|
||||||
) ? (
|
|
||||||
// Menampilkan gambar
|
|
||||||
<img
|
|
||||||
className="object-fill h-full w-full rounded-md"
|
|
||||||
src={data.url}
|
|
||||||
alt={data.fileName || "File"}
|
|
||||||
/>
|
|
||||||
) : data.format === ".pdf" ? (
|
|
||||||
// Menampilkan PDF menggunakan iframe
|
|
||||||
<iframe
|
|
||||||
className="w-full h-96 rounded-md"
|
|
||||||
src={data.url}
|
|
||||||
title={data.fileName || "PDF File"}
|
|
||||||
/>
|
|
||||||
) : [".docx", ".ppt", ".pptx"].includes(data.format) ? (
|
|
||||||
// Menampilkan file dokumen menggunakan Office Viewer
|
|
||||||
<iframe
|
|
||||||
className="w-full h-96 rounded-md"
|
|
||||||
src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
|
|
||||||
data.url
|
|
||||||
)}`}
|
|
||||||
title={data.fileName || "Document"}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
// Menampilkan link jika format tidak dikenali
|
|
||||||
<a
|
|
||||||
href={data.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="block text-blue-500 underline"
|
|
||||||
>
|
|
||||||
View {data.fileName || "File"}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</SwiperSlide>
|
|
||||||
))}
|
|
||||||
</Swiper>
|
|
||||||
<div className="mt-2 ">
|
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
slidesPerView={8}
|
modules={[FreeMode, Navigation, Thumbs]}
|
||||||
spaceBetween={8}
|
navigation={false}
|
||||||
pagination={{ clickable: true }}
|
className="w-full"
|
||||||
modules={[Pagination, Thumbs]}
|
|
||||||
>
|
>
|
||||||
{detailThumb?.map((data: any, index: number) => (
|
{detailThumb?.map((data: any, index: number) => (
|
||||||
<SwiperSlide key={index}>
|
<SwiperSlide key={index}>
|
||||||
{[".jpg", ".jpeg", ".png", ".webp"].includes(
|
{[".jpg", ".jpeg", ".png", ".webp"].includes(
|
||||||
data.format
|
data.format
|
||||||
) ? (
|
) ? (
|
||||||
|
// Menampilkan gambar
|
||||||
<img
|
<img
|
||||||
className="object-cover h-[60px] w-[80px]"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data.url}
|
src={data.url}
|
||||||
alt={data.fileName}
|
alt={data.fileName || "File"}
|
||||||
|
/>
|
||||||
|
) : data.format === ".pdf" ? (
|
||||||
|
// Menampilkan PDF menggunakan iframe
|
||||||
|
<iframe
|
||||||
|
className="w-full h-96 rounded-md"
|
||||||
|
src={data.url}
|
||||||
|
title={data.fileName || "PDF File"}
|
||||||
|
/>
|
||||||
|
) : [".docx", ".ppt", ".pptx"].includes(
|
||||||
|
data.format
|
||||||
|
) ? (
|
||||||
|
// Menampilkan file dokumen menggunakan Office Viewer
|
||||||
|
<iframe
|
||||||
|
className="w-full h-96 rounded-md"
|
||||||
|
src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
|
||||||
|
data.url
|
||||||
|
)}`}
|
||||||
|
title={data.fileName || "Document"}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-[60px] w-[80px] flex items-center justify-center bg-gray-200 text-sm text-center text-gray-700 rounded-md">
|
// Menampilkan link jika format tidak dikenali
|
||||||
{data?.format?.replace(".", "").toUpperCase()}
|
<a
|
||||||
</div>
|
href={data.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block text-blue-500 underline"
|
||||||
|
>
|
||||||
|
View {data.fileName || "File"}
|
||||||
|
</a>
|
||||||
)}
|
)}
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
<div className="mt-2 ">
|
||||||
|
<Swiper
|
||||||
|
onSwiper={setThumbsSwiper}
|
||||||
|
slidesPerView={8}
|
||||||
|
spaceBetween={8}
|
||||||
|
pagination={{ clickable: true }}
|
||||||
|
modules={[Pagination, Thumbs]}
|
||||||
|
>
|
||||||
|
{detailThumb?.map((data: any, index: number) => (
|
||||||
|
<SwiperSlide key={index}>
|
||||||
|
{[".jpg", ".jpeg", ".png", ".webp"].includes(
|
||||||
|
data.format
|
||||||
|
) ? (
|
||||||
|
<img
|
||||||
|
className="object-cover h-[60px] w-[80px]"
|
||||||
|
src={data.url}
|
||||||
|
alt={data.fileName}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="h-[60px] w-[80px] flex items-center justify-center bg-gray-200 text-sm text-center text-gray-700 rounded-md">
|
||||||
|
{data?.format?.replace(".", "").toUpperCase()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -570,7 +574,7 @@ export default function FormTeksDetail() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -603,7 +607,7 @@ export default function FormTeksDetail() {
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{detail?.tags
|
{detail?.tags
|
||||||
?.split(",")
|
?.split(",")
|
||||||
|
|
@ -619,8 +623,8 @@ export default function FormTeksDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="5"
|
id="5"
|
||||||
|
|
@ -657,10 +661,10 @@ export default function FormTeksDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 border mx-3">
|
<div className="px-3 py-3 border mx-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||||
</div>
|
</div>
|
||||||
{/* {detail?.isPublish == false ? (
|
{/* {detail?.isPublish == false ? (
|
||||||
|
|
@ -680,14 +684,15 @@ export default function FormTeksDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:check" className="mr-3" /> Setujui
|
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("3")}
|
onClick={() => actionApproval("3")}
|
||||||
className="bg-orange-400 hover:bg-orange-300"
|
className="bg-orange-400 hover:bg-orange-300"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:comment-o" className="mr-3" /> Revisi
|
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||||
|
{t("revision")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("4")}
|
onClick={() => actionApproval("4")}
|
||||||
|
|
@ -695,7 +700,7 @@ export default function FormTeksDetail() {
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:times" className="mr-3" />
|
<Icon icon="fa:times" className="mr-3" />
|
||||||
Tolak
|
{t("reject")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -706,7 +711,7 @@ export default function FormTeksDetail() {
|
||||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||||
<DialogContent size="md">
|
<DialogContent size="md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>{t("leave-comment")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file, index) => (
|
? files?.map((file, index) => (
|
||||||
|
|
@ -743,7 +748,7 @@ export default function FormTeksDetail() {
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
>
|
>
|
||||||
Semua
|
{t("all")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -888,7 +893,7 @@ export default function FormTeksDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -897,7 +902,7 @@ export default function FormTeksDetail() {
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ import { Item } from "@radix-ui/react-dropdown-menu";
|
||||||
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 { useTranslations } from "next-intl";
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -115,7 +116,7 @@ export default function FormTeks() {
|
||||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||||
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
|
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [unitSelection, setUnitSelection] = useState({
|
const [unitSelection, setUnitSelection] = useState({
|
||||||
allUnit: false,
|
allUnit: false,
|
||||||
|
|
@ -707,11 +708,11 @@ export default function FormTeks() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Teks</p>
|
<p className="text-lg font-semibold mb-3">{t("form-text")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -731,8 +732,8 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -757,7 +758,7 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3 py-2">
|
<div className="flex flex-row items-center gap-3 py-2">
|
||||||
<Label>Bantuan AI</Label>
|
<Label>{t("ai-assistance")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch
|
<Switch
|
||||||
defaultChecked={isSwitchOn}
|
defaultChecked={isSwitchOn}
|
||||||
|
|
@ -773,7 +774,7 @@ export default function FormTeks() {
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Bahasa</Label>
|
<Label>{t("language")}</Label>
|
||||||
<Select onValueChange={setSelectedLanguage}>
|
<Select onValueChange={setSelectedLanguage}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -785,7 +786,7 @@ export default function FormTeks() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Writing Style</Label>
|
<Label>{t("writing-style")}</Label>
|
||||||
<Select onValueChange={setSelectedWritingStyle}>
|
<Select onValueChange={setSelectedWritingStyle}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -804,7 +805,7 @@ export default function FormTeks() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Article Size</Label>
|
<Label>{t("article-size")}</Label>
|
||||||
<Select onValueChange={setSelectedSize}>
|
<Select onValueChange={setSelectedSize}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -825,7 +826,7 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Main Keyword</Label>
|
<Label>{t("main-keyword")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -849,7 +850,7 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -871,7 +872,7 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>SEO</Label>
|
<Label>{t("seo")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -882,15 +883,9 @@ export default function FormTeks() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-semibold">
|
<p className="font-semibold">
|
||||||
Kata kunci untuk disertakan dalam teks
|
{t("Keywords to include in the text")}
|
||||||
</p>
|
|
||||||
<p className="text-sm">
|
|
||||||
JIka Anda tidak Memberikan kata kunci, kami akan secara
|
|
||||||
otomatis membuat kata kunci yang relevan dari kata kunci
|
|
||||||
utama untuk setiap bagian dan menggunakannya untuk membuat
|
|
||||||
artikel. Untuk menambahkan kata kunci baru, ketik ',
|
|
||||||
+ kata kunci'.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm">{t("title-key")}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Textarea
|
<Textarea
|
||||||
value={selectedSEO}
|
value={selectedSEO}
|
||||||
|
|
@ -900,7 +895,7 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Instruksi Khusus (Optional)</Label>
|
<Label>{t("special-instructions")} (Optional)</Label>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -957,7 +952,7 @@ export default function FormTeks() {
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
Edit
|
{t("update")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
@ -966,8 +961,8 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="">
|
<div className="space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -990,8 +985,8 @@ export default function FormTeks() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -1004,11 +999,10 @@ export default function FormTeks() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan format .doc, .docx, .pdf, .ppt,
|
{t("upload-file-text-max")}
|
||||||
atau .pptx Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1017,7 +1011,7 @@ export default function FormTeks() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1026,7 +1020,7 @@ export default function FormTeks() {
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={handleRemoveAllFiles}
|
onClick={handleRemoveAllFiles}
|
||||||
>
|
>
|
||||||
Remove All
|
{t("remove-all")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
@ -1042,7 +1036,7 @@ export default function FormTeks() {
|
||||||
<Card className=" h-[500px]">
|
<Card className=" h-[500px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -1063,8 +1057,8 @@ export default function FormTeks() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3 space-y-2">
|
||||||
<Label htmlFor="tags">Tags</Label>
|
<Label htmlFor="tags">{t("tags")}</Label>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -1092,8 +1086,8 @@ export default function FormTeks() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -1116,12 +1110,12 @@ export default function FormTeks() {
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ import Image from "next/image";
|
||||||
import { error, loading } from "@/lib/swal";
|
import { error, loading } from "@/lib/swal";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const teksSchema = z.object({
|
const teksSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -87,6 +89,13 @@ type Option = {
|
||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormTeksUpdate() {
|
export default function FormTeksUpdate() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -108,6 +117,7 @@ export default function FormTeksUpdate() {
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
const scheduleType = Cookies.get("scheduleType");
|
const scheduleType = Cookies.get("scheduleType");
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||||
const [tags, setTags] = useState<any[]>([]);
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
|
|
@ -555,13 +565,11 @@ export default function FormTeksUpdate() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">
|
<p className="text-lg font-semibold mb-3">{t("form-text")}</p>
|
||||||
Form Update Konten Teks
|
|
||||||
</p>
|
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -582,8 +590,8 @@ export default function FormTeksUpdate() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={detail?.categoryId.name} // Nilai default berdasarkan detail
|
defaultValue={detail?.categoryId.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -604,17 +612,15 @@ export default function FormTeksUpdate() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor
|
||||||
ref={editor}
|
|
||||||
value={detail?.description}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
initialData={detail?.description || value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -624,8 +630,8 @@ export default function FormTeksUpdate() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -638,11 +644,10 @@ export default function FormTeksUpdate() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan format .doc, .docx, .pdf, .ppt,
|
{t("upload-file-text-max")}
|
||||||
atau .pptx Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -651,7 +656,7 @@ export default function FormTeksUpdate() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -660,14 +665,17 @@ export default function FormTeksUpdate() {
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={handleRemoveAllFiles}
|
onClick={handleRemoveAllFiles}
|
||||||
>
|
>
|
||||||
Remove All
|
{t("remove-all")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4 space-y-2">
|
||||||
<Label className="text-lg font-semibold">File</Label>
|
<Label className="text-lg font-semibold">
|
||||||
|
{" "}
|
||||||
|
{t("file-media")}
|
||||||
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any) => (
|
{files.map((file: any) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -688,7 +696,7 @@ export default function FormTeksUpdate() {
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-blue-500 text-sm"
|
className="text-blue-500 text-sm"
|
||||||
>
|
>
|
||||||
Lihat File
|
{t("view-file")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -706,7 +714,7 @@ export default function FormTeksUpdate() {
|
||||||
}
|
}
|
||||||
className="form-checkbox"
|
className="form-checkbox"
|
||||||
/>
|
/>
|
||||||
<span>Semua</span>
|
<span>{t("all")}</span>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -778,7 +786,7 @@ export default function FormTeksUpdate() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -811,7 +819,7 @@ export default function FormTeksUpdate() {
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="tags"
|
id="tags"
|
||||||
|
|
@ -846,7 +854,7 @@ export default function FormTeksUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -867,22 +875,22 @@ export default function FormTeksUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
{/* <p>{detail?.status}</p> */}
|
{/* <p>{detail?.status}</p> */}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useRouter } from "@/i18n/routing";
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -135,6 +136,7 @@ export default function FormVideoDetail() {
|
||||||
const [detailVideo, setDetailVideo] = useState<any>([]);
|
const [detailVideo, setDetailVideo] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
|
|
@ -412,13 +414,11 @@ export default function FormVideoDetail() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">
|
<p className="text-lg font-semibold mb-3">{t("form-video")}</p>
|
||||||
Form Detail Konten Video
|
|
||||||
</p>
|
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -439,8 +439,8 @@ export default function FormVideoDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={detail?.category.name} // Nilai default berdasarkan detail
|
value={detail?.category.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -462,8 +462,8 @@ export default function FormVideoDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -477,48 +477,49 @@ export default function FormVideoDetail() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
<Label className="text-xl text-black">File Mediaa</Label>
|
<Label className="text-xl "> {t("file-media")}</Label>
|
||||||
<div className="w-full ">
|
<div className="w-full ">
|
||||||
<Swiper
|
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
|
||||||
modules={[FreeMode, Navigation, Thumbs]}
|
|
||||||
navigation={false}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{detailVideo?.map((data: any) => (
|
|
||||||
<SwiperSlide key={data.id}>
|
|
||||||
<video
|
|
||||||
className="object-fill h-full w-full"
|
|
||||||
src={data}
|
|
||||||
controls
|
|
||||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
|
||||||
/>
|
|
||||||
</SwiperSlide>
|
|
||||||
))}
|
|
||||||
</Swiper>
|
|
||||||
<div className=" mt-2 ">
|
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
slidesPerView={6}
|
modules={[FreeMode, Navigation, Thumbs]}
|
||||||
spaceBetween={8}
|
navigation={false}
|
||||||
pagination={{
|
className="w-full"
|
||||||
clickable: true,
|
|
||||||
}}
|
|
||||||
modules={[Pagination, Thumbs]}
|
|
||||||
// className="mySwiper2"
|
|
||||||
>
|
>
|
||||||
{detailVideo?.map((data: any) => (
|
{detailVideo?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide key={data.id}>
|
||||||
<video
|
<video
|
||||||
className="object-cover h-[60px] w-[80px]"
|
className="object-fill h-full w-full"
|
||||||
src={data}
|
src={data}
|
||||||
muted
|
controls
|
||||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||||
/>
|
/>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
<div className=" mt-2 ">
|
||||||
|
<Swiper
|
||||||
|
onSwiper={setThumbsSwiper}
|
||||||
|
slidesPerView={6}
|
||||||
|
spaceBetween={8}
|
||||||
|
pagination={{
|
||||||
|
clickable: true,
|
||||||
|
}}
|
||||||
|
modules={[Pagination, Thumbs]}
|
||||||
|
// className="mySwiper2"
|
||||||
|
>
|
||||||
|
{detailVideo?.map((data: any) => (
|
||||||
|
<SwiperSlide key={data.id}>
|
||||||
|
<video
|
||||||
|
className="object-cover h-[60px] w-[80px]"
|
||||||
|
src={data}
|
||||||
|
muted
|
||||||
|
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -528,7 +529,7 @@ export default function FormVideoDetail() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -549,8 +550,8 @@ export default function FormVideoDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 px-3">
|
<div className="mt-3 px-3 space-y-2">
|
||||||
<Label>Pratinjau Gambar Utama</Label>
|
<Label>{t("preview")}</Label>
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
<img
|
<img
|
||||||
src={detail.thumbnailLink}
|
src={detail.thumbnailLink}
|
||||||
|
|
@ -561,7 +562,7 @@ export default function FormVideoDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{detail?.tags
|
{detail?.tags
|
||||||
?.split(",")
|
?.split(",")
|
||||||
|
|
@ -577,8 +578,8 @@ export default function FormVideoDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="5"
|
id="5"
|
||||||
|
|
@ -615,10 +616,10 @@ export default function FormVideoDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 border mx-3">
|
<div className="px-3 py-3 border mx-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||||
</div>
|
</div>
|
||||||
{/* {detail?.isPublish == false ? (
|
{/* {detail?.isPublish == false ? (
|
||||||
|
|
@ -638,14 +639,15 @@ export default function FormVideoDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:check" className="mr-3" /> Setujui
|
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("3")}
|
onClick={() => actionApproval("3")}
|
||||||
className="bg-orange-400 hover:bg-orange-300"
|
className="bg-orange-400 hover:bg-orange-300"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:comment-o" className="mr-3" /> Revisi
|
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||||
|
{t("revision")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => actionApproval("4")}
|
onClick={() => actionApproval("4")}
|
||||||
|
|
@ -653,7 +655,7 @@ export default function FormVideoDetail() {
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Icon icon="fa:times" className="mr-3" />
|
<Icon icon="fa:times" className="mr-3" />
|
||||||
Tolak
|
{t("reject")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -664,7 +666,7 @@ export default function FormVideoDetail() {
|
||||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||||
<DialogContent size="md">
|
<DialogContent size="md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>{t("leave-comment")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file, index) => (
|
? files?.map((file, index) => (
|
||||||
|
|
@ -701,7 +703,7 @@ export default function FormVideoDetail() {
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
>
|
>
|
||||||
Semua
|
{t("all")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -846,14 +848,14 @@ export default function FormVideoDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={() => setModalOpen(false)}
|
onClick={() => setModalOpen(false)}
|
||||||
>
|
>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ import { Item } from "@radix-ui/react-dropdown-menu";
|
||||||
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 { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -88,6 +89,7 @@ export default function FormVideo() {
|
||||||
const scheduleType = Cookies.get("scheduleType");
|
const scheduleType = Cookies.get("scheduleType");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||||
const [tags, setTags] = useState<any[]>([]);
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
|
|
@ -706,10 +708,10 @@ export default function FormVideo() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Konten Video</p>
|
<p className="text-lg font-semibold mb-3">{t("form-video")}</p>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -729,8 +731,8 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -755,7 +757,7 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3 py-2">
|
<div className="flex flex-row items-center gap-3 py-2">
|
||||||
<Label>Bantuan AI</Label>
|
<Label>{t("ai-assistance")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch
|
<Switch
|
||||||
defaultChecked={isSwitchOn}
|
defaultChecked={isSwitchOn}
|
||||||
|
|
@ -771,7 +773,7 @@ export default function FormVideo() {
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Bahasa</Label>
|
<Label>{t("language")}</Label>
|
||||||
<Select onValueChange={setSelectedLanguage}>
|
<Select onValueChange={setSelectedLanguage}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -783,7 +785,7 @@ export default function FormVideo() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Writing Style</Label>
|
<Label>{t("writing-style")}</Label>
|
||||||
<Select onValueChange={setSelectedWritingStyle}>
|
<Select onValueChange={setSelectedWritingStyle}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -802,7 +804,7 @@ export default function FormVideo() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 py-3 w-4/12">
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
<Label>Article Size</Label>
|
<Label>{t("article-size")}</Label>
|
||||||
<Select onValueChange={setSelectedSize}>
|
<Select onValueChange={setSelectedSize}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -823,7 +825,7 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Main Keyword</Label>
|
<Label>{t("main-keyword")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -847,7 +849,7 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -869,7 +871,7 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="flex flex-row items-center gap-3 mb-3">
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
<Label>SEO</Label>
|
<Label>{t("seo")}</Label>
|
||||||
<Button
|
<Button
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -880,15 +882,9 @@ export default function FormVideo() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-semibold">
|
<p className="font-semibold">
|
||||||
Kata kunci untuk disertakan dalam teks
|
{t("Keywords to include in the text")}
|
||||||
</p>
|
|
||||||
<p className="text-sm">
|
|
||||||
JIka Anda tidak Memberikan kata kunci, kami akan secara
|
|
||||||
otomatis membuat kata kunci yang relevan dari kata kunci
|
|
||||||
utama untuk setiap bagian dan menggunakannya untuk membuat
|
|
||||||
artikel. Untuk menambahkan kata kunci baru, ketik ',
|
|
||||||
+ kata kunci'.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm">{t("title-key")}</p>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Textarea
|
<Textarea
|
||||||
value={selectedSEO}
|
value={selectedSEO}
|
||||||
|
|
@ -898,7 +894,7 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Instruksi Khusus (Optional)</Label>
|
<Label>{t("special-instructions")} (Optional)</Label>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -955,7 +951,7 @@ export default function FormVideo() {
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
Edit
|
{t("update")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
@ -964,8 +960,8 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="">
|
<div className="space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -988,8 +984,8 @@ export default function FormVideo() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -1002,11 +998,10 @@ export default function FormVideo() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan mp4 atau mov Ukuran maksimal
|
{t("upload-file-video-max")}
|
||||||
100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1015,7 +1010,7 @@ export default function FormVideo() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1024,7 +1019,7 @@ export default function FormVideo() {
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={handleRemoveAllFiles}
|
onClick={handleRemoveAllFiles}
|
||||||
>
|
>
|
||||||
Remove All
|
{t("remove-all")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
@ -1040,7 +1035,7 @@ export default function FormVideo() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -1074,8 +1069,8 @@ export default function FormVideo() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3 space-y-2">
|
||||||
<Label htmlFor="tags">Tags</Label>
|
<Label htmlFor="tags">{t("tags")}</Label>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -1103,8 +1098,8 @@ export default function FormVideo() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -1127,12 +1122,12 @@ export default function FormVideo() {
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
import { error, loading } from "@/lib/swal";
|
import { error, loading } from "@/lib/swal";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const videoSchema = z.object({
|
const videoSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -94,6 +96,13 @@ type Option = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormVideoUpdate() {
|
export default function FormVideoUpdate() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -110,6 +119,7 @@ export default function FormVideoUpdate() {
|
||||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||||
const [counterProgress, setCounterProgress] = useState(0);
|
const [counterProgress, setCounterProgress] = useState(0);
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
|
|
@ -609,13 +619,11 @@ export default function FormVideoUpdate() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">
|
<p className="text-lg font-semibold mb-3">{t("form-video")}</p>
|
||||||
Form Update Konten Video
|
|
||||||
</p>
|
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -636,8 +644,8 @@ export default function FormVideoUpdate() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full space-y-2">
|
||||||
<Label>Kategori</Label>
|
<Label>{t("category")}</Label>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={detail?.categoryId.name} // Nilai default berdasarkan detail
|
defaultValue={detail?.categoryId.name} // Nilai default berdasarkan detail
|
||||||
onValueChange={(id) => {
|
onValueChange={(id) => {
|
||||||
|
|
@ -659,17 +667,15 @@ export default function FormVideoUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Deskripsi</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor
|
||||||
ref={editor}
|
|
||||||
value={detail?.description}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
initialData={detail?.description || value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -679,8 +685,8 @@ export default function FormVideoUpdate() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="py-3 space-y-2">
|
||||||
<Label>Pilih File</Label>
|
<Label>{t("select-file")}</Label>
|
||||||
{/* <Input
|
{/* <Input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -693,11 +699,10 @@ export default function FormVideoUpdate() {
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
{/* Drop files here or click to upload. */}
|
{/* Drop files here or click to upload. */}
|
||||||
Tarik file disini atau klik untuk upload.
|
{t("drag-file")}
|
||||||
</h4>
|
</h4>
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" text-xs text-muted-foreground">
|
||||||
( Upload file dengan format .jpg, .jpeg, atau .png.
|
{t("upload-file-video-max")}
|
||||||
Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -706,7 +711,7 @@ export default function FormVideoUpdate() {
|
||||||
<div>{fileList}</div>
|
<div>{fileList}</div>
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<div className="flex flex-row items-center gap-3 py-3">
|
<div className="flex flex-row items-center gap-3 py-3">
|
||||||
<Label>Gunakan Watermark</Label>
|
<Label>{t("watermark")}</Label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Switch defaultChecked color="primary" id="c2" />
|
<Switch defaultChecked color="primary" id="c2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -721,8 +726,11 @@ export default function FormVideoUpdate() {
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4 space-y-2">
|
||||||
<Label className="text-lg font-semibold">File</Label>
|
<Label className="text-lg font-semibold">
|
||||||
|
{" "}
|
||||||
|
{t("file-media")}
|
||||||
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any) => (
|
{files.map((file: any) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -743,7 +751,7 @@ export default function FormVideoUpdate() {
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-blue-500 text-sm"
|
className="text-blue-500 text-sm"
|
||||||
>
|
>
|
||||||
Lihat File
|
{t("view-file")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -761,7 +769,7 @@ export default function FormVideoUpdate() {
|
||||||
}
|
}
|
||||||
className="form-checkbox"
|
className="form-checkbox"
|
||||||
/>
|
/>
|
||||||
<span>Semua</span>
|
<span>{t("all")}</span>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -833,7 +841,7 @@ export default function FormVideoUpdate() {
|
||||||
<Card className=" h-[800px]">
|
<Card className=" h-[800px]">
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kreator</Label>
|
<Label>{t("creator")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="creatorName"
|
name="creatorName"
|
||||||
|
|
@ -854,8 +862,8 @@ export default function FormVideoUpdate() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 px-3">
|
<div className="mt-3 px-3 space-y-2">
|
||||||
<Label>Pratinjau Gambar Utama</Label>
|
<Label>{t("preview")}</Label>
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
<img
|
<img
|
||||||
src={detail.thumbnailLink}
|
src={detail.thumbnailLink}
|
||||||
|
|
@ -866,7 +874,7 @@ export default function FormVideoUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>{t("tags")}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="tags"
|
id="tags"
|
||||||
|
|
@ -900,8 +908,8 @@ export default function FormVideoUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6 space-y-2">
|
||||||
<Label>Target Publish</Label>
|
<Label>{t("publish-target")}</Label>
|
||||||
{options.map((option: Option) => (
|
{options.map((option: Option) => (
|
||||||
<div key={option.id} className="flex gap-2 items-center">
|
<div key={option.id} className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -921,22 +929,22 @@ export default function FormVideoUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<p className="">Kotak Saran (0)</p>
|
<p className="">{t("suggestion-box")} (0)</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<p>Keterangan:</p>
|
<p>{t("information")}:</p>
|
||||||
{/* <p>{detail?.status}</p> */}
|
{/* <p>{detail?.status}</p> */}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="flex flex-row justify-end gap-3">
|
<div className="flex flex-row justify-end gap-3">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import {
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { CalendarIcon, ChevronDown, ChevronUp } from "lucide-react";
|
import { CalendarIcon, ChevronDown, ChevronUp, Trash2 } from "lucide-react";
|
||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import { DateRange } from "react-day-picker";
|
import { DateRange } from "react-day-picker";
|
||||||
|
|
@ -898,10 +898,10 @@ export default function FormContestDetail() {
|
||||||
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||||
{/* Display remaining time */}
|
{/* Display remaining time */}
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<h2 className="text-lg font-bold">Link Berita</h2>
|
<Label className="">Link Berita</Label>
|
||||||
{links.map((link, index) => (
|
{links.map((link, index) => (
|
||||||
<div key={index} className="flex items-center gap-2 mt-2">
|
<div key={index} className="flex items-center gap-2 mt-2">
|
||||||
<input
|
<Input
|
||||||
type="url"
|
type="url"
|
||||||
className="border rounded p-2 w-full"
|
className="border rounded p-2 w-full"
|
||||||
placeholder={`Masukkan link berita ${index + 1}`}
|
placeholder={`Masukkan link berita ${index + 1}`}
|
||||||
|
|
@ -916,18 +916,19 @@ export default function FormContestDetail() {
|
||||||
className="bg-red-500 text-white px-3 py-1 rounded"
|
className="bg-red-500 text-white px-3 py-1 rounded"
|
||||||
onClick={() => handleRemoveRow(index)}
|
onClick={() => handleRemoveRow(index)}
|
||||||
>
|
>
|
||||||
Hapus
|
<Trash2 className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
size="md"
|
||||||
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
|
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
|
||||||
onClick={handleAddRow}
|
onClick={handleAddRow}
|
||||||
>
|
>
|
||||||
Tambah Link
|
Tambah Link
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ import {
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { AlertDialogHeader } from "@/components/ui/alert-dialog";
|
import { AlertDialogHeader } from "@/components/ui/alert-dialog";
|
||||||
import { Description } from "@radix-ui/react-toast";
|
import { Description } from "@radix-ui/react-toast";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -61,6 +63,13 @@ export type mediahubDetail = {
|
||||||
is_active: string;
|
is_active: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function PublishMediahub() {
|
export default function PublishMediahub() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -75,6 +84,7 @@ export default function PublishMediahub() {
|
||||||
image: false,
|
image: false,
|
||||||
text: false,
|
text: false,
|
||||||
});
|
});
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [mainType, setMainType] = useState<number>(1);
|
const [mainType, setMainType] = useState<number>(1);
|
||||||
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
||||||
const [broadcastType, setBroadcastType] = useState<string>("all"); // untuk Tipe Penugasan
|
const [broadcastType, setBroadcastType] = useState<string>("all"); // untuk Tipe Penugasan
|
||||||
|
|
@ -304,13 +314,13 @@ export default function PublishMediahub() {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Perencanaan Mediahub</p>
|
<p className="text-lg font-semibold mb-3">{t("planning-mediahub")}</p>
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -328,8 +338,8 @@ export default function PublishMediahub() {
|
||||||
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Output Tugas</Label>
|
<Label>{t("output-tugas")}</Label>
|
||||||
<div className="flex flex-wrap gap-3 mt-1">
|
<div className="flex flex-wrap gap-3 mt-1">
|
||||||
{Object.keys(taskOutput).map((key) => (
|
{Object.keys(taskOutput).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
|
@ -347,8 +357,8 @@ export default function PublishMediahub() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Pelaksana Tugas</Label>
|
<Label>{t("executive-task")}</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="">
|
<div className="">
|
||||||
|
|
@ -451,8 +461,8 @@ export default function PublishMediahub() {
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>{t("assignment-type")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail?.assignmentType?.id.toString()}
|
value={detail?.assignmentType?.id.toString()}
|
||||||
onValueChange={(value) => setType(value)}
|
onValueChange={(value) => setType(value)}
|
||||||
|
|
@ -473,8 +483,8 @@ export default function PublishMediahub() {
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label>Date</Label>
|
<Label>{t("date")}</Label>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
defaultValue={detail?.date}
|
defaultValue={detail?.date}
|
||||||
|
|
@ -495,17 +505,15 @@ export default function PublishMediahub() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Narasi Penugasan</Label>
|
<Label>{t("description-task")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor
|
||||||
ref={editor}
|
|
||||||
value={detail?.description}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
initialData={detail?.description || value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -520,7 +528,7 @@ export default function PublishMediahub() {
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -55,6 +57,13 @@ export type medsosDetail = {
|
||||||
is_active: string;
|
is_active: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function PublishMedsos() {
|
export default function PublishMedsos() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -69,6 +78,7 @@ export default function PublishMedsos() {
|
||||||
image: false,
|
image: false,
|
||||||
text: false,
|
text: false,
|
||||||
});
|
});
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [mainType, setMainType] = useState<number>(1);
|
const [mainType, setMainType] = useState<number>(1);
|
||||||
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
||||||
const [broadcastType, setBroadcastType] = useState<string>("all"); // untuk Tipe Penugasan
|
const [broadcastType, setBroadcastType] = useState<string>("all"); // untuk Tipe Penugasan
|
||||||
|
|
@ -297,13 +307,13 @@ export default function PublishMedsos() {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Perencanaan Mediahub</p>
|
<p className="text-lg font-semibold mb-3">{t("planning-medsos")}</p>
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -322,7 +332,7 @@ export default function PublishMedsos() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<Label>Output Tugas</Label>
|
<Label>{t("output-tugas")}</Label>
|
||||||
<div className="flex flex-wrap gap-3 mt-1">
|
<div className="flex flex-wrap gap-3 mt-1">
|
||||||
{Object.keys(taskOutput).map((key) => (
|
{Object.keys(taskOutput).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
|
@ -340,8 +350,8 @@ export default function PublishMedsos() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Pelaksana Tugas</Label>
|
<Label>{t("executive-task")}</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -367,7 +377,7 @@ export default function PublishMedsos() {
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="soft" size="sm" color="primary">
|
<Button variant="soft" size="sm" color="primary">
|
||||||
[Kustom]
|
[{t("custom")}]
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||||
|
|
@ -444,8 +454,8 @@ export default function PublishMedsos() {
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>{t("assignment-type")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup
|
value={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup
|
||||||
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
||||||
|
|
@ -465,9 +475,9 @@ export default function PublishMedsos() {
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Label>Date</Label>
|
<Label>{t("date")}</Label>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
value={detail.date}
|
value={detail.date}
|
||||||
|
|
@ -488,17 +498,15 @@ export default function PublishMedsos() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Narasi Penugasan</Label>
|
<Label>{t("description-task")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor
|
||||||
ref={editor}
|
|
||||||
value={detail?.description}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
initialData={detail?.description || value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -513,7 +521,7 @@ export default function PublishMedsos() {
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ export default function FormEventDetail() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-6 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-6 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -245,11 +245,11 @@ export default function FormEventDetail() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -276,7 +276,7 @@ export default function FormEventDetail() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -326,7 +326,7 @@ export default function FormEventDetail() {
|
||||||
DI SAMPAIKAN OLEH
|
DI SAMPAIKAN OLEH
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -349,7 +349,7 @@ export default function FormEventDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ export default function FormEvent() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -242,11 +242,11 @@ export default function FormEvent() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -273,7 +273,7 @@ export default function FormEvent() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -321,7 +321,7 @@ export default function FormEvent() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -344,7 +344,7 @@ export default function FormEvent() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,7 @@ export default function FormEventUpdate() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -258,11 +258,11 @@ export default function FormEventUpdate() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -289,7 +289,7 @@ export default function FormEventUpdate() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -337,7 +337,7 @@ export default function FormEventUpdate() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -360,7 +360,7 @@ export default function FormEventUpdate() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ export default function FormDetailPressRillis() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-6 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-6 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -255,11 +255,11 @@ export default function FormDetailPressRillis() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -286,7 +286,7 @@ export default function FormDetailPressRillis() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -332,7 +332,7 @@ export default function FormDetailPressRillis() {
|
||||||
{errors.location?.message}
|
{errors.location?.message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Invitation</Label>
|
<Label>Invitation</Label>
|
||||||
<Select onValueChange={setSelectedTarget}>
|
<Select onValueChange={setSelectedTarget}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
|
|
@ -353,7 +353,7 @@ export default function FormDetailPressRillis() {
|
||||||
DI SAMPAIKAN OLEH
|
DI SAMPAIKAN OLEH
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -376,7 +376,7 @@ export default function FormDetailPressRillis() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ export default function FormUpdatePressRelease() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -265,11 +265,11 @@ export default function FormUpdatePressRelease() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -296,7 +296,7 @@ export default function FormUpdatePressRelease() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -342,7 +342,7 @@ export default function FormUpdatePressRelease() {
|
||||||
{errors.location?.message}
|
{errors.location?.message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Invitation</Label>
|
<Label>Invitation</Label>
|
||||||
<Select onValueChange={setSelectedTarget}>
|
<Select onValueChange={setSelectedTarget}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
|
|
@ -361,7 +361,7 @@ export default function FormUpdatePressRelease() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -384,7 +384,7 @@ export default function FormUpdatePressRelease() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ export default function FormPressRelease() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -242,11 +242,11 @@ export default function FormPressRelease() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -273,7 +273,7 @@ export default function FormPressRelease() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -319,7 +319,7 @@ export default function FormPressRelease() {
|
||||||
{errors.location?.message}
|
{errors.location?.message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Invitation</Label>
|
<Label>Invitation</Label>
|
||||||
<Select onValueChange={setSelectedTarget}>
|
<Select onValueChange={setSelectedTarget}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
|
|
@ -338,7 +338,7 @@ export default function FormPressRelease() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -361,7 +361,7 @@ export default function FormPressRelease() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ export default function FormDetailPressConference() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between mt-6">
|
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between mt-6">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -202,11 +202,11 @@ export default function FormDetailPressConference() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -233,7 +233,7 @@ export default function FormDetailPressConference() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -283,7 +283,7 @@ export default function FormDetailPressConference() {
|
||||||
DI SAMPAIKAN OLEH
|
DI SAMPAIKAN OLEH
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -306,7 +306,7 @@ export default function FormDetailPressConference() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -207,20 +207,20 @@ export default function FormPressConference() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mb-4 mt-2 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mb-4 mt-2 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild className="px-0">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[250px] justify-start text-left font-normal border border-slate-300",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal border border-slate-300 px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -247,7 +247,7 @@ export default function FormPressConference() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -295,7 +295,7 @@ export default function FormPressConference() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm mt-4 font-semibold">DI SAMPAIKAN OLEH</p>
|
<p className="text-sm mt-4 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -318,7 +318,7 @@ export default function FormPressConference() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ export default function FormUpdatePressConference() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
<div className="flex flex-col lg:flex-row mt-3 items-start lg:items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col space-y-2">
|
||||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
@ -262,11 +262,11 @@ export default function FormUpdatePressConference() {
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[280px] lg:w-[300px] justify-start text-left font-normal",
|
"w-[280px] lg:w-[250px] justify-start text-left font-normal px-0 md:px-0 lg:px-4",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon size={15} className="mr-3" />
|
||||||
{date?.from ? (
|
{date?.from ? (
|
||||||
date.to ? (
|
date.to ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -293,7 +293,7 @@ export default function FormUpdatePressConference() {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Rentang Waktu</Label>
|
<Label htmlFor="title">Rentang Waktu</Label>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -343,7 +343,7 @@ export default function FormUpdatePressConference() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
<p className="text-sm my-2 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col ">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Pangkat</Label>
|
<Label>Nama Pangkat</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -366,7 +366,7 @@ export default function FormUpdatePressConference() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col my-3">
|
<div className="flex flex-col my-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1 space-y-2">
|
||||||
<Label>Nama Lengkap</Label>
|
<Label>Nama Lengkap</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,944 @@
|
||||||
|
"use client";
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import * as z from "zod";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import JoditEditor from "jodit-react";
|
||||||
|
import {
|
||||||
|
createTask,
|
||||||
|
createTaskTa,
|
||||||
|
getTask,
|
||||||
|
getUserLevelForAssignments,
|
||||||
|
} from "@/service/task";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { ChevronDown, ChevronUp, Trash2 } from "lucide-react";
|
||||||
|
import { AudioRecorder } from "react-audio-voice-recorder";
|
||||||
|
import FileUploader from "@/components/form/shared/file-uploader";
|
||||||
|
import { Upload } from "tus-js-client";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
import { loading } from "@/lib/swal";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const taskSchema = z.object({
|
||||||
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
naration: z.string().min(2, {
|
||||||
|
message: "Narasi Penugasan harus lebih dari 2 karakter.",
|
||||||
|
}),
|
||||||
|
// url: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
interface FileWithPreview extends File {
|
||||||
|
preview: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type taskDetail = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
fileTypeOutput: string;
|
||||||
|
assignedToTopLevel: string;
|
||||||
|
assignedToLevel: string;
|
||||||
|
assignmentType: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
assignmentMainType: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
attachmentUrl: string;
|
||||||
|
taskType: string;
|
||||||
|
broadcastType: string;
|
||||||
|
narration: string;
|
||||||
|
is_active: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function FormTaskTa() {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const editor = useRef(null);
|
||||||
|
type TaskSchema = z.infer<typeof taskSchema>;
|
||||||
|
const { id } = useParams() as { id: string };
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
// State for various form fields
|
||||||
|
const [expertise, setExpertiseOutput] = useState({
|
||||||
|
semua: false,
|
||||||
|
komunikasi: false,
|
||||||
|
hukum: false,
|
||||||
|
bahasa: false,
|
||||||
|
ekonomi: false,
|
||||||
|
politik: false,
|
||||||
|
sosiologi: false,
|
||||||
|
ilmuadministrasipemerintah: false,
|
||||||
|
ti: false,
|
||||||
|
});
|
||||||
|
const [expert, setExpertOutput] = useState({
|
||||||
|
semua: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// const [assignmentType, setAssignmentType] = useState("mediahub");
|
||||||
|
// const [assignmentCategory, setAssignmentCategory] = useState("publication");
|
||||||
|
const [mainType, setMainType] = useState<string>("1");
|
||||||
|
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
||||||
|
const [broadcastType, setBroadcastType] = useState<string>("");
|
||||||
|
const [type, setType] = useState<string>("1");
|
||||||
|
const [selectedTarget, setSelectedTarget] = useState("3,4");
|
||||||
|
const [detail, setDetail] = useState<taskDetail>();
|
||||||
|
const [refresh] = useState(false);
|
||||||
|
const [listDest, setListDest] = useState([]);
|
||||||
|
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
||||||
|
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||||
|
const [isRecording, setIsRecording] = useState(false);
|
||||||
|
const [timer, setTimer] = useState<number>(120);
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
|
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
||||||
|
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
|
||||||
|
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
|
||||||
|
const [audioFiles, setAudioFiles] = useState<FileWithPreview[]>([]);
|
||||||
|
const [isImageUploadFinish, setIsImageUploadFinish] = useState(false);
|
||||||
|
const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false);
|
||||||
|
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
|
||||||
|
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
|
||||||
|
const [voiceNoteLink, setVoiceNoteLink] = useState("");
|
||||||
|
|
||||||
|
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||||
|
const [unitSelection, setUnitSelection] = useState({
|
||||||
|
semua: false,
|
||||||
|
mabes: false,
|
||||||
|
polda: false,
|
||||||
|
polres: false,
|
||||||
|
satker: false,
|
||||||
|
});
|
||||||
|
const [links, setLinks] = useState<string[]>([""]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
setValue,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<TaskSchema>({
|
||||||
|
resolver: zodResolver(taskSchema),
|
||||||
|
mode: "all",
|
||||||
|
});
|
||||||
|
|
||||||
|
// const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// const selectedValue = Number(event.target.value);
|
||||||
|
// setMainType(selectedValue);
|
||||||
|
|
||||||
|
// setPlatformTypeVisible(selectedValue === 2);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchPoldaPolres() {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await getUserLevelForAssignments();
|
||||||
|
setListDest(response?.data?.data.list);
|
||||||
|
console.log("polda", response?.data?.data?.list);
|
||||||
|
const initialExpandedState = response?.data?.data.list.reduce(
|
||||||
|
(acc: any, polda: any) => {
|
||||||
|
acc[polda.id] = false;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
setExpandedPolda(initialExpandedState);
|
||||||
|
console.log("polres", initialExpandedState);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching Polda/Polres data:", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchPoldaPolres();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// };
|
||||||
|
const handleCheckboxChange = (levelId: number) => {
|
||||||
|
setCheckedLevels((prev) => {
|
||||||
|
const updatedLevels = new Set(prev);
|
||||||
|
if (updatedLevels.has(levelId)) {
|
||||||
|
updatedLevels.delete(levelId);
|
||||||
|
} else {
|
||||||
|
updatedLevels.add(levelId);
|
||||||
|
}
|
||||||
|
return updatedLevels;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePoldaPolresChange = () => {
|
||||||
|
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUnitChange = (
|
||||||
|
key: keyof typeof unitSelection,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "semua") {
|
||||||
|
const newState = {
|
||||||
|
semua: value,
|
||||||
|
mabes: value,
|
||||||
|
polda: value,
|
||||||
|
polres: value,
|
||||||
|
satker: value,
|
||||||
|
};
|
||||||
|
setUnitSelection(newState);
|
||||||
|
} else {
|
||||||
|
const updatedSelection = {
|
||||||
|
...unitSelection,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["mabes", "polda", "polres", "satker"].every(
|
||||||
|
(k) => updatedSelection[k as keyof typeof unitSelection]
|
||||||
|
);
|
||||||
|
|
||||||
|
updatedSelection.semua = allChecked;
|
||||||
|
|
||||||
|
setUnitSelection(updatedSelection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExpertiseOutputChange = (
|
||||||
|
key: keyof typeof expertise,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "semua") {
|
||||||
|
const newState = {
|
||||||
|
semua: value,
|
||||||
|
komunikasi: value,
|
||||||
|
hukum: value,
|
||||||
|
bahasa: value,
|
||||||
|
ekonomi: value,
|
||||||
|
politik: value,
|
||||||
|
sosiologi: value,
|
||||||
|
ilmuadministrasipemerintah: value,
|
||||||
|
ti: value,
|
||||||
|
};
|
||||||
|
setExpertiseOutput(newState);
|
||||||
|
} else {
|
||||||
|
const updated = {
|
||||||
|
...expertise,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["video", "audio", "image", "text"].every(
|
||||||
|
(k) => updated[k as keyof typeof expertise]
|
||||||
|
);
|
||||||
|
|
||||||
|
updated.semua = allChecked;
|
||||||
|
setExpertiseOutput(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExpertOutputChange = (
|
||||||
|
key: keyof typeof expert,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "semua") {
|
||||||
|
const newState = {
|
||||||
|
semua: value,
|
||||||
|
};
|
||||||
|
setExpertOutput(newState);
|
||||||
|
} else {
|
||||||
|
const updated = {
|
||||||
|
...expert,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["video", "audio", "image", "text"].every(
|
||||||
|
(k) => updated[k as keyof typeof expert]
|
||||||
|
);
|
||||||
|
|
||||||
|
updated.semua = allChecked;
|
||||||
|
setExpertOutput(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async (data: TaskSchema) => {
|
||||||
|
const fileTypeMapping = {
|
||||||
|
all: "1",
|
||||||
|
video: "2",
|
||||||
|
audio: "4",
|
||||||
|
image: "3",
|
||||||
|
text: "5",
|
||||||
|
};
|
||||||
|
|
||||||
|
const unitMapping = {
|
||||||
|
allUnit: "0",
|
||||||
|
mabes: "1",
|
||||||
|
polda: "2",
|
||||||
|
polres: "3",
|
||||||
|
satker: "4",
|
||||||
|
};
|
||||||
|
const assignmentPurposeString = Object.keys(unitSelection)
|
||||||
|
.filter((key) => unitSelection[key as keyof typeof unitSelection])
|
||||||
|
.map((key) => unitMapping[key as keyof typeof unitMapping])
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
const selectedOutputs = Object.keys(expertise)
|
||||||
|
.filter((key) => expertise[key as keyof typeof expertise])
|
||||||
|
.map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping])
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
const requestData: {
|
||||||
|
id?: number;
|
||||||
|
title: string;
|
||||||
|
assignedToLevel: any;
|
||||||
|
assignedToUsers: any;
|
||||||
|
assignmentTypeId: string;
|
||||||
|
fileTypeOutput: string;
|
||||||
|
narration: string;
|
||||||
|
platformType: string | null;
|
||||||
|
assignmentMainTypeId: any;
|
||||||
|
assignmentType: string;
|
||||||
|
assignedToRole: string;
|
||||||
|
broadcastType: string;
|
||||||
|
expertCompetencies: string;
|
||||||
|
attachmentUrl: string[];
|
||||||
|
} = {
|
||||||
|
...data,
|
||||||
|
// assignmentType,
|
||||||
|
// assignmentCategory,
|
||||||
|
assignedToLevel: handlePoldaPolresChange(),
|
||||||
|
assignedToUsers: assignmentPurposeString,
|
||||||
|
assignedToRole: selectedTarget,
|
||||||
|
assignmentType: taskType,
|
||||||
|
broadcastType: broadcastType,
|
||||||
|
assignmentMainTypeId: mainType,
|
||||||
|
assignmentTypeId: type,
|
||||||
|
fileTypeOutput: selectedOutputs,
|
||||||
|
narration: data.naration,
|
||||||
|
platformType: "",
|
||||||
|
expertCompetencies: "1,2,3",
|
||||||
|
title: data.title,
|
||||||
|
attachmentUrl: links,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await createTaskTa(requestData);
|
||||||
|
|
||||||
|
console.log("Form Data Submitted:", requestData);
|
||||||
|
console.log("response", response);
|
||||||
|
|
||||||
|
const id = response?.data?.data.id;
|
||||||
|
loading();
|
||||||
|
if (imageFiles?.length == 0) {
|
||||||
|
setIsImageUploadFinish(true);
|
||||||
|
}
|
||||||
|
imageFiles?.map(async (item: any, index: number) => {
|
||||||
|
await uploadResumableFile(index, String(id), item, "1", "0");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (videoFiles?.length == 0) {
|
||||||
|
setIsVideoUploadFinish(true);
|
||||||
|
}
|
||||||
|
videoFiles?.map(async (item: any, index: number) => {
|
||||||
|
await uploadResumableFile(index, String(id), item, "2", "0");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (textFiles?.length == 0) {
|
||||||
|
setIsTextUploadFinish(true);
|
||||||
|
}
|
||||||
|
textFiles?.map(async (item: any, index: number) => {
|
||||||
|
await uploadResumableFile(index, String(id), item, "3", "0");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (audioFiles?.length == 0) {
|
||||||
|
setIsAudioUploadFinish(true);
|
||||||
|
}
|
||||||
|
audioFiles.map(async (item: FileWithPreview, index: number) => {
|
||||||
|
await uploadResumableFile(
|
||||||
|
index,
|
||||||
|
String(id),
|
||||||
|
item, // Use .file to access the actual File object
|
||||||
|
"4",
|
||||||
|
"0" // Optional: Replace with actual duration if available
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = (data: TaskSchema) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Simpan Data",
|
||||||
|
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
save(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleExpand = (poldaId: any) => {
|
||||||
|
setExpandedPolda((prev: any) => ({
|
||||||
|
...prev,
|
||||||
|
[poldaId]: !prev[poldaId],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRecordingStart = () => {
|
||||||
|
setIsRecording(true);
|
||||||
|
|
||||||
|
const countdown = setInterval(() => {
|
||||||
|
setTimer((prevTimer) => {
|
||||||
|
if (prevTimer <= 1) {
|
||||||
|
clearInterval(countdown);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return prevTimer - 1;
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isRecording) {
|
||||||
|
handleStopRecording();
|
||||||
|
}
|
||||||
|
}, 120000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStopRecording = () => {
|
||||||
|
setIsRecording(false);
|
||||||
|
setTimer(120); // Reset the timer to 2 minutes for the next recording
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAudioElement = (blob: Blob) => {
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const audio = document.createElement("audio");
|
||||||
|
audio.src = url;
|
||||||
|
audio.controls = true;
|
||||||
|
document.body.appendChild(audio);
|
||||||
|
|
||||||
|
// Convert Blob to File and add preview
|
||||||
|
const fileWithPreview: FileWithPreview = Object.assign(
|
||||||
|
new File([blob], "voiceNote.webm", { type: "audio/webm" }),
|
||||||
|
{ preview: url }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add to state
|
||||||
|
setAudioFile(fileWithPreview);
|
||||||
|
setAudioFiles((prev) => [...prev, fileWithPreview]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteAudio = (index: number) => {
|
||||||
|
setAudioFiles((prev) => prev.filter((_, idx) => idx !== index));
|
||||||
|
};
|
||||||
|
|
||||||
|
async function uploadResumableFile(
|
||||||
|
idx: number,
|
||||||
|
id: string,
|
||||||
|
file: any,
|
||||||
|
fileTypeId: string,
|
||||||
|
duration: string
|
||||||
|
) {
|
||||||
|
console.log(idx, id, file, fileTypeId, duration);
|
||||||
|
|
||||||
|
const resCsrf = await getCsrfToken();
|
||||||
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
console.log("CSRF TOKEN : ", csrfToken);
|
||||||
|
const headers = {
|
||||||
|
"X-XSRF-TOKEN": csrfToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
const upload = new Upload(file, {
|
||||||
|
endpoint: `${process.env.NEXT_PUBLIC_API}/assignment/file/upload`,
|
||||||
|
headers: headers,
|
||||||
|
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||||
|
chunkSize: 20_000,
|
||||||
|
metadata: {
|
||||||
|
assignmentId: id,
|
||||||
|
filename: file.name,
|
||||||
|
contentType: file.type,
|
||||||
|
fileTypeId: fileTypeId,
|
||||||
|
duration,
|
||||||
|
},
|
||||||
|
onBeforeRequest: function (req) {
|
||||||
|
var xhr = req.getUnderlyingObject();
|
||||||
|
xhr.withCredentials = true;
|
||||||
|
},
|
||||||
|
onError: async (e: any) => {
|
||||||
|
console.log("Error upload :", e);
|
||||||
|
error(e);
|
||||||
|
},
|
||||||
|
onChunkComplete: (
|
||||||
|
chunkSize: any,
|
||||||
|
bytesAccepted: any,
|
||||||
|
bytesTotal: any
|
||||||
|
) => {
|
||||||
|
// const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||||
|
// progressInfo[idx].percentage = uploadPersen;
|
||||||
|
// counterUpdateProgress++;
|
||||||
|
// console.log(counterUpdateProgress);
|
||||||
|
// setProgressList(progressInfo);
|
||||||
|
// setCounterProgress(counterUpdateProgress);
|
||||||
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
// uploadPersen = 100;
|
||||||
|
// progressInfo[idx].percentage = 100;
|
||||||
|
// counterUpdateProgress++;
|
||||||
|
// setCounterProgress(counterUpdateProgress);
|
||||||
|
successTodo();
|
||||||
|
if (fileTypeId == "1") {
|
||||||
|
setIsImageUploadFinish(true);
|
||||||
|
} else if (fileTypeId == "2") {
|
||||||
|
setIsVideoUploadFinish(true);
|
||||||
|
}
|
||||||
|
if (fileTypeId == "3") {
|
||||||
|
setIsTextUploadFinish(true);
|
||||||
|
}
|
||||||
|
if (fileTypeId == "4") {
|
||||||
|
setIsAudioUploadFinish(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
upload.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
successTodo();
|
||||||
|
}, [
|
||||||
|
isImageUploadFinish,
|
||||||
|
isVideoUploadFinish,
|
||||||
|
isAudioUploadFinish,
|
||||||
|
isTextUploadFinish,
|
||||||
|
]);
|
||||||
|
|
||||||
|
function successTodo() {
|
||||||
|
if (
|
||||||
|
isImageUploadFinish &&
|
||||||
|
isVideoUploadFinish &&
|
||||||
|
isAudioUploadFinish &&
|
||||||
|
isTextUploadFinish
|
||||||
|
) {
|
||||||
|
successSubmit("/in/contributor/task");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const successSubmit = (redirect: string) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
text: "Data berhasil disimpan.",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then(() => {
|
||||||
|
router.push(redirect);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLinkChange = (index: number, value: string) => {
|
||||||
|
const updatedLinks = [...links];
|
||||||
|
updatedLinks[index] = value;
|
||||||
|
setLinks(updatedLinks);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddRow = () => {
|
||||||
|
setLinks([...links, ""]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove a specific link row
|
||||||
|
const handleRemoveRow = (index: number) => {
|
||||||
|
const updatedLinks = links.filter((_: any, i: any) => i !== index);
|
||||||
|
setLinks(updatedLinks);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<div className="px-6 py-6">
|
||||||
|
<p className="text-lg font-semibold mb-3">{t("form-task")}</p>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="gap-5 mb-5">
|
||||||
|
{/* Input Title */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>{t("title")}</Label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Input
|
||||||
|
size="md"
|
||||||
|
type="text"
|
||||||
|
value={detail?.title}
|
||||||
|
onChange={field.onChange}
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{errors.title?.message && (
|
||||||
|
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
||||||
|
<div className="mt-5 space-y-2">
|
||||||
|
<Label>{t("assignment-selection")}</Label>
|
||||||
|
<Select onValueChange={setSelectedTarget}>
|
||||||
|
<SelectTrigger size="md">
|
||||||
|
<SelectValue placeholder="Choose" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="3,4">Semua Pengguna</SelectItem>
|
||||||
|
<SelectItem value="4">Kontributor</SelectItem>
|
||||||
|
<SelectItem value="3">Approver</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-3 mt-5 lg:pt-7 lg:ml-3 ">
|
||||||
|
{Object.keys(unitSelection).map((key) => (
|
||||||
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
<Checkbox
|
||||||
|
id={key}
|
||||||
|
checked={unitSelection[key as keyof typeof unitSelection]}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
handleUnitChange(
|
||||||
|
key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={key}>
|
||||||
|
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-6 lg:pt-6 lg:pl-3">
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="soft" size="sm" color="primary">
|
||||||
|
[{t("custom")}]
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Daftar Wilayah Polda dan Polres</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||||
|
{listDest.map((polda: any) => (
|
||||||
|
<div key={polda.id} className="border p-2">
|
||||||
|
<Label className="flex items-center">
|
||||||
|
<Checkbox
|
||||||
|
checked={checkedLevels.has(polda.id)}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleCheckboxChange(polda.id)
|
||||||
|
}
|
||||||
|
className="mr-3"
|
||||||
|
/>
|
||||||
|
{polda.name}
|
||||||
|
<button
|
||||||
|
onClick={() => toggleExpand(polda.id)}
|
||||||
|
className="ml-2 focus:outline-none"
|
||||||
|
>
|
||||||
|
{expandedPolda[polda.id] ? (
|
||||||
|
<ChevronUp size={16} />
|
||||||
|
) : (
|
||||||
|
<ChevronDown size={16} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</Label>
|
||||||
|
{expandedPolda[polda.id] && (
|
||||||
|
<div className="ml-6 mt-2">
|
||||||
|
<Label className="block">
|
||||||
|
<Checkbox
|
||||||
|
checked={polda?.subDestination?.every(
|
||||||
|
(polres: any) =>
|
||||||
|
checkedLevels.has(polres.id)
|
||||||
|
)}
|
||||||
|
onCheckedChange={(isChecked) => {
|
||||||
|
const updatedLevels = new Set(
|
||||||
|
checkedLevels
|
||||||
|
);
|
||||||
|
polda?.subDestination?.forEach(
|
||||||
|
(polres: any) => {
|
||||||
|
if (isChecked) {
|
||||||
|
updatedLevels.add(polres.id);
|
||||||
|
} else {
|
||||||
|
updatedLevels.delete(polres.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setCheckedLevels(updatedLevels);
|
||||||
|
}}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
Pilih Semua Polres
|
||||||
|
</Label>
|
||||||
|
{polda?.subDestination?.map((polres: any) => (
|
||||||
|
<Label key={polres.id} className="block mt-1">
|
||||||
|
<Checkbox
|
||||||
|
checked={checkedLevels.has(polres.id)}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleCheckboxChange(polres.id)
|
||||||
|
}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
{polres.name}
|
||||||
|
</Label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 space-y-2">
|
||||||
|
<Label>{t("assigment-type")} </Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={taskType}
|
||||||
|
onValueChange={(value) => setTaskType(String(value))}
|
||||||
|
className="flex flex-wrap gap-3"
|
||||||
|
>
|
||||||
|
<RadioGroupItem value="atensi-khusus" id="khusus" />
|
||||||
|
<Label htmlFor="atensi-khusus">Atensi Khusus</Label>
|
||||||
|
<RadioGroupItem value="tugas-harian" id="harian" />
|
||||||
|
<Label htmlFor="tugas-harian">Tugas Harian</Label>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 space-y-2">
|
||||||
|
<Label>{t("areas-expertise")}</Label>
|
||||||
|
<div className="flex flex-wrap gap-4">
|
||||||
|
{Object.keys(expertise).map((key) => (
|
||||||
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
<Checkbox
|
||||||
|
id={key}
|
||||||
|
checked={expertise[key as keyof typeof expertise]}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
handleExpertiseOutputChange(
|
||||||
|
key as keyof typeof expertise,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={key}>
|
||||||
|
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 space-y-2">
|
||||||
|
<Label>{t("choose-expert")}</Label>
|
||||||
|
<div className="flex flex-wrap gap-4">
|
||||||
|
{Object.keys(expert).map((key) => (
|
||||||
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
<Checkbox
|
||||||
|
id={key}
|
||||||
|
checked={expert[key as keyof typeof expert]}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
handleExpertOutputChange(
|
||||||
|
key as keyof typeof expert,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={key}>
|
||||||
|
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* <div className="mt-5">
|
||||||
|
<Label>Broadcast </Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={broadcastType} // Nilai terpilih diambil dari state broadcastType
|
||||||
|
onValueChange={(value) => setBroadcastType(value)} // Mengatur nilai saat radio berubah
|
||||||
|
className="flex flex-wrap gap-3"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="all" id="all" />
|
||||||
|
<Label htmlFor="all">Semua</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="email" id="email" />
|
||||||
|
<Label htmlFor="email">Email Blast</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="whatsapp" id="whatsapp" />
|
||||||
|
<Label htmlFor="whatsapp">WhatsApp Blast</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</div> */}
|
||||||
|
<div className="mt-5 space-y-2">
|
||||||
|
<Label>{t("description")}</Label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="naration"
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{errors.naration?.message && (
|
||||||
|
<p className="text-red-400 text-sm">
|
||||||
|
{errors.naration.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2.5 mt-5">
|
||||||
|
<Label htmlFor="attachments">{t("attachment")}</Label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div>
|
||||||
|
<Label>{t("audio-visual")}</Label>
|
||||||
|
<FileUploader
|
||||||
|
accept={{
|
||||||
|
"mp4/*": [],
|
||||||
|
"mov/*": [],
|
||||||
|
}}
|
||||||
|
maxSize={100}
|
||||||
|
label="Upload file dengan format .mp4 atau .mov."
|
||||||
|
onDrop={(files) => setVideoFiles(files)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>{t("image")}</Label>
|
||||||
|
<FileUploader
|
||||||
|
accept={{
|
||||||
|
"image/*": [],
|
||||||
|
}}
|
||||||
|
maxSize={100}
|
||||||
|
label="Upload file dengan format .png, .jpg, atau .jpeg."
|
||||||
|
onDrop={(files) => setImageFiles(files)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>{t("text")}</Label>
|
||||||
|
<FileUploader
|
||||||
|
accept={{
|
||||||
|
"pdf/*": [],
|
||||||
|
}}
|
||||||
|
maxSize={100}
|
||||||
|
label="Upload file dengan format .pdf."
|
||||||
|
onDrop={(files) => setTextFiles(files)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>{t("audio")}</Label>
|
||||||
|
<AudioRecorder
|
||||||
|
onRecordingComplete={addAudioElement}
|
||||||
|
audioTrackConstraints={{
|
||||||
|
noiseSuppression: true,
|
||||||
|
echoCancellation: true,
|
||||||
|
}}
|
||||||
|
downloadOnSavePress={true}
|
||||||
|
downloadFileExtension="webm"
|
||||||
|
/>
|
||||||
|
<FileUploader
|
||||||
|
accept={{
|
||||||
|
"mp3/*": [],
|
||||||
|
"wav/*": [],
|
||||||
|
}}
|
||||||
|
maxSize={100}
|
||||||
|
label="Upload file dengan format .mp3 atau .wav."
|
||||||
|
onDrop={(files) =>
|
||||||
|
setAudioFiles((prev) => [...prev, ...files])
|
||||||
|
}
|
||||||
|
className="mt-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{audioFiles?.map((audio: any, idx: any) => (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="flex flex-row justify-between items-center"
|
||||||
|
>
|
||||||
|
<p>{t("voice-note")}</p>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleDeleteAudio(idx)}
|
||||||
|
size="sm"
|
||||||
|
color="destructive"
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||||
|
{/* Display remaining time */}
|
||||||
|
<div className="mt-4 space-y-2">
|
||||||
|
<Label className="">{t("news-links")}</Label>
|
||||||
|
{links.map((link, index) => (
|
||||||
|
<div key={index} className="flex items-center gap-2 mt-2">
|
||||||
|
<Input
|
||||||
|
type="url"
|
||||||
|
className="border rounded p-2 w-full"
|
||||||
|
placeholder={`Masukkan link berita ${index + 1}`}
|
||||||
|
value={link}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleLinkChange(index, e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{links.length > 1 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-red-500 text-white px-3 py-1 rounded"
|
||||||
|
onClick={() => handleRemoveRow(index)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
|
||||||
|
onClick={handleAddRow}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
{t("add-links")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<div className="mt-4">
|
||||||
|
<Button type="submit" color="primary">
|
||||||
|
{t("submit")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -66,6 +66,7 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import WavesurferPlayer from "@wavesurfer/react";
|
import WavesurferPlayer from "@wavesurfer/react";
|
||||||
import WaveSurfer from "wavesurfer.js";
|
import WaveSurfer from "wavesurfer.js";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -206,6 +207,7 @@ export default function FormTaskDetail() {
|
||||||
text: false,
|
text: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [uploadResults, setUploadResults] = useState<UploadResult[]>([]);
|
const [uploadResults, setUploadResults] = useState<UploadResult[]>([]);
|
||||||
const [isTableResult, setIsTableResult] = useState(false);
|
const [isTableResult, setIsTableResult] = useState(false);
|
||||||
const [isSentResult] = useState(false);
|
const [isSentResult] = useState(false);
|
||||||
|
|
@ -812,7 +814,7 @@ export default function FormTaskDetail() {
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row justify-between">
|
<div className="flex flex-col sm:flex-row lg:flex-row justify-between">
|
||||||
<p className="text-lg font-semibold mb-3">Detail Penugasan</p>
|
<p className="text-lg font-semibold mb-3">{t("detail-task")}</p>
|
||||||
<div
|
<div
|
||||||
className="flex gap-3"
|
className="flex gap-3"
|
||||||
style={
|
style={
|
||||||
|
|
@ -830,7 +832,7 @@ export default function FormTaskDetail() {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => setModalType("terkirim")}
|
onClick={() => setModalType("terkirim")}
|
||||||
>
|
>
|
||||||
{sentAcceptance?.length} Terkirim
|
{sentAcceptance?.length} {t("sent")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
|
|
@ -840,13 +842,15 @@ export default function FormTaskDetail() {
|
||||||
onClick={() => setModalType("diterima")}
|
onClick={() => setModalType("diterima")}
|
||||||
className="ml-3"
|
className="ml-3"
|
||||||
>
|
>
|
||||||
{acceptAcceptance?.length} Diterima
|
{acceptAcceptance?.length} {t("accepted")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Detail Status Penugasan</DialogTitle>
|
<DialogTitle>
|
||||||
|
{t("assignment-status-details")}
|
||||||
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{modalType === "terkirim" && getModalContent("terkirim")}
|
{modalType === "terkirim" && getModalContent("terkirim")}
|
||||||
|
|
@ -860,7 +864,7 @@ export default function FormTaskDetail() {
|
||||||
<form>
|
<form>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kode Unik</Label>
|
<Label>{t("unique-code")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="uniqueCode"
|
name="uniqueCode"
|
||||||
|
|
@ -877,7 +881,7 @@ export default function FormTaskDetail() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 mt-6">
|
<div className="space-y-2 mt-6">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -896,8 +900,8 @@ export default function FormTaskDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Tujuan Pemilihan Tugas</Label>
|
<Label>{t("assignment-selection")}</Label>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={setSelectedTarget}
|
onValueChange={setSelectedTarget}
|
||||||
value={detail.assignedToRole}
|
value={detail.assignedToRole}
|
||||||
|
|
@ -935,7 +939,7 @@ export default function FormTaskDetail() {
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="soft" size="sm" color="primary">
|
<Button variant="soft" size="sm" color="primary">
|
||||||
[Kustom]
|
[{t("custom")}]
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||||
|
|
@ -1018,8 +1022,8 @@ export default function FormTaskDetail() {
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Tipe Penugasan</Label>
|
<Label>{t("type-task")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail.assignmentMainType.id.toString()}
|
value={detail.assignmentMainType.id.toString()}
|
||||||
onValueChange={(value) => setMainType(value)}
|
onValueChange={(value) => setMainType(value)}
|
||||||
|
|
@ -1033,8 +1037,8 @@ export default function FormTaskDetail() {
|
||||||
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Jenis Tugas </Label>
|
<Label>{t("assigment-type")} </Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail.taskType.toString()}
|
value={detail.taskType.toString()}
|
||||||
onValueChange={(value) => setTaskType(String(value))}
|
onValueChange={(value) => setTaskType(String(value))}
|
||||||
|
|
@ -1047,8 +1051,8 @@ export default function FormTaskDetail() {
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
{/* RadioGroup Assignment Category */}
|
{/* RadioGroup Assignment Category */}
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>{t("type-of-task")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail.assignmentType.id.toString()}
|
value={detail.assignmentType.id.toString()}
|
||||||
onValueChange={(value) => setType(value)}
|
onValueChange={(value) => setType(value)}
|
||||||
|
|
@ -1068,8 +1072,8 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Output Tugas</Label>
|
<Label>{t("output-task")}</Label>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
{Object.keys(taskOutput).map((key) => (
|
{Object.keys(taskOutput).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
|
@ -1089,8 +1093,8 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6 space-y-2">
|
||||||
<Label>Narasi Penugasan</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="naration"
|
name="naration"
|
||||||
|
|
@ -1104,11 +1108,13 @@ export default function FormTaskDetail() {
|
||||||
</p>
|
</p>
|
||||||
)} */}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 mt-5">
|
<div className=" mt-5 space-y-2">
|
||||||
<Label htmlFor="attachment">Lampiran</Label>
|
<Label htmlFor="attachment">{t("attachment")}</Label>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
{videoUploadedFiles?.length > 0 && <Label>Video</Label>}
|
{videoUploadedFiles?.length > 0 && (
|
||||||
|
<Label>{t("audio-visual")}</Label>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
{selectedVideo && (
|
{selectedVideo && (
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
|
|
@ -1154,7 +1160,9 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{imageUploadedFiles?.length > 0 && <Label>Foto</Label>}
|
{imageUploadedFiles?.length > 0 && (
|
||||||
|
<Label>{t("image")}</Label>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
{selectedImage && (
|
{selectedImage && (
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
|
|
@ -1200,7 +1208,9 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{textUploadedFiles?.length > 0 && <Label>Teks</Label>}
|
{textUploadedFiles?.length > 0 && (
|
||||||
|
<Label>{t("text")}</Label>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
{selectedText && (
|
{selectedText && (
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
|
|
@ -1247,7 +1257,9 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{audioUploadedFiles?.length > 0 && <Label>Audio</Label>}
|
{audioUploadedFiles?.length > 0 && (
|
||||||
|
<Label>{t("audio")}</Label>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
{selectedAudio && (
|
{selectedAudio && (
|
||||||
<Card className="mt-2">
|
<Card className="mt-2">
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ import WavesurferPlayer from "@wavesurfer/react";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
import { error, loading } from "@/lib/swal";
|
import { error, loading } from "@/lib/swal";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
// uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
// uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -93,6 +95,13 @@ type Url = {
|
||||||
attachmentUrl: string;
|
attachmentUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormTaskEdit() {
|
export default function FormTaskEdit() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -110,6 +119,7 @@ export default function FormTaskEdit() {
|
||||||
text: false,
|
text: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
||||||
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
|
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
|
||||||
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
|
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
|
||||||
|
|
@ -294,6 +304,63 @@ export default function FormTaskEdit() {
|
||||||
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUnitChange = (
|
||||||
|
key: keyof typeof unitSelection,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "allUnit") {
|
||||||
|
const newState = {
|
||||||
|
allUnit: value,
|
||||||
|
mabes: value,
|
||||||
|
polda: value,
|
||||||
|
polres: value,
|
||||||
|
satker: value,
|
||||||
|
};
|
||||||
|
setUnitSelection(newState);
|
||||||
|
} else {
|
||||||
|
const updatedSelection = {
|
||||||
|
...unitSelection,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["mabes", "polda", "polres", "satker"].every(
|
||||||
|
(k) => updatedSelection[k as keyof typeof unitSelection]
|
||||||
|
);
|
||||||
|
|
||||||
|
updatedSelection.allUnit = allChecked;
|
||||||
|
|
||||||
|
setUnitSelection(updatedSelection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTaskOutputChange = (
|
||||||
|
key: keyof typeof taskOutput,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "all") {
|
||||||
|
const newState = {
|
||||||
|
all: value,
|
||||||
|
video: value,
|
||||||
|
audio: value,
|
||||||
|
image: value,
|
||||||
|
text: value,
|
||||||
|
};
|
||||||
|
setTaskOutput(newState);
|
||||||
|
} else {
|
||||||
|
const updated = {
|
||||||
|
...taskOutput,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["video", "audio", "image", "text"].every(
|
||||||
|
(k) => updated[k as keyof typeof taskOutput]
|
||||||
|
);
|
||||||
|
|
||||||
|
updated.all = allChecked;
|
||||||
|
setTaskOutput(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const save = async (data: TaskSchema) => {
|
const save = async (data: TaskSchema) => {
|
||||||
const fileTypeMapping = {
|
const fileTypeMapping = {
|
||||||
all: "1",
|
all: "1",
|
||||||
|
|
@ -624,7 +691,7 @@ export default function FormTaskEdit() {
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="space-y-2 mt-6">
|
<div className="space-y-2">
|
||||||
<Label>Judul</Label>
|
<Label>Judul</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -644,7 +711,7 @@ export default function FormTaskEdit() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
||||||
<div className="mt-6">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Tujuan Pemilihan Tugas</Label>
|
<Label>Tujuan Pemilihan Tugas</Label>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={setSelectedTarget}
|
onValueChange={setSelectedTarget}
|
||||||
|
|
@ -660,7 +727,7 @@ export default function FormTaskEdit() {
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-3 mt-6 lg:pt-5 lg:ml-3">
|
<div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3">
|
||||||
{Object.keys(unitSelection).map((key) => (
|
{Object.keys(unitSelection).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -669,7 +736,10 @@ export default function FormTaskEdit() {
|
||||||
unitSelection[key as keyof typeof unitSelection]
|
unitSelection[key as keyof typeof unitSelection]
|
||||||
}
|
}
|
||||||
onCheckedChange={(value) =>
|
onCheckedChange={(value) =>
|
||||||
setUnitSelection({ ...unitSelection, [key]: value })
|
handleUnitChange(
|
||||||
|
key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={key}>
|
<Label htmlFor={key}>
|
||||||
|
|
@ -678,7 +748,7 @@ export default function FormTaskEdit() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 lg:pt-5 lg:pl-3">
|
<div className="mt-6 lg:pt-6 lg:pl-3">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="soft" size="sm" color="primary">
|
<Button variant="soft" size="sm" color="primary">
|
||||||
|
|
@ -762,8 +832,8 @@ export default function FormTaskEdit() {
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Tipe Penugasan</Label>
|
<Label>{t("type-task")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
defaultValue={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup
|
defaultValue={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup
|
||||||
onValueChange={(value) => setMainType(value)}
|
onValueChange={(value) => setMainType(value)}
|
||||||
|
|
@ -777,8 +847,8 @@ export default function FormTaskEdit() {
|
||||||
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Jenis Tugas </Label>
|
<Label>{t("assigment-type")} </Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
defaultValue={detail.taskType.toString()}
|
defaultValue={detail.taskType.toString()}
|
||||||
onValueChange={(value) => setTaskType(String(value))}
|
onValueChange={(value) => setTaskType(String(value))}
|
||||||
|
|
@ -791,8 +861,8 @@ export default function FormTaskEdit() {
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
{/* RadioGroup Assignment Category */}
|
{/* RadioGroup Assignment Category */}
|
||||||
<div className="mt-6">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>{t("type-of-task")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
defaultValue={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup
|
defaultValue={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup
|
||||||
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
||||||
|
|
@ -812,16 +882,19 @@ export default function FormTaskEdit() {
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Output Tugas</Label>
|
<Label>{t("output-task")}</Label>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-4">
|
||||||
{Object.keys(taskOutput).map((key) => (
|
{Object.keys(taskOutput).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={key}
|
id={key}
|
||||||
checked={taskOutput[key as keyof typeof taskOutput]}
|
checked={taskOutput[key as keyof typeof taskOutput]}
|
||||||
onCheckedChange={(value) =>
|
onCheckedChange={(value) =>
|
||||||
setTaskOutput({ ...taskOutput, [key]: value })
|
handleTaskOutputChange(
|
||||||
|
key as keyof typeof taskOutput,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={key}>
|
<Label htmlFor={key}>
|
||||||
|
|
@ -831,17 +904,15 @@ export default function FormTaskEdit() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Narasi Penugasan</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="naration"
|
name="naration"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor
|
||||||
ref={editor}
|
|
||||||
value={detail?.narration}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
initialData={detail?.narration || value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -851,11 +922,11 @@ export default function FormTaskEdit() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 mt-5">
|
<div className="space-y-2.5 mt-5">
|
||||||
<Label htmlFor="attachments">Lampiran</Label>
|
<Label htmlFor="attachments">{t("attachment")}</Label>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Video</Label>
|
<Label>{t("audio-visual")}</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"mp4/*": [],
|
"mp4/*": [],
|
||||||
|
|
@ -895,8 +966,8 @@ export default function FormTaskEdit() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Foto</Label>
|
<Label>{t("image")}</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"image/*": [],
|
"image/*": [],
|
||||||
|
|
@ -935,8 +1006,8 @@ export default function FormTaskEdit() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Teks</Label>
|
<Label>{t("text")}</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"pdf/*": [],
|
"pdf/*": [],
|
||||||
|
|
@ -975,8 +1046,8 @@ export default function FormTaskEdit() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Audio</Label>
|
<Label>{t("audio")}</Label>
|
||||||
<AudioRecorder
|
<AudioRecorder
|
||||||
onRecordingComplete={addAudioElement}
|
onRecordingComplete={addAudioElement}
|
||||||
audioTrackConstraints={{
|
audioTrackConstraints={{
|
||||||
|
|
@ -1030,7 +1101,7 @@ export default function FormTaskEdit() {
|
||||||
</div>
|
</div>
|
||||||
{audioFile && (
|
{audioFile && (
|
||||||
<div className="flex flex-row justify-between items-center">
|
<div className="flex flex-row justify-between items-center">
|
||||||
<p>Voice Note</p>
|
<p>{t("voice-note")}</p>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleDeleteAudio}
|
onClick={handleDeleteAudio}
|
||||||
|
|
@ -1043,8 +1114,8 @@ export default function FormTaskEdit() {
|
||||||
)}
|
)}
|
||||||
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||||
{/* Display remaining time */}
|
{/* Display remaining time */}
|
||||||
<div className="mt-4">
|
<div className="mt-4 space-y-2">
|
||||||
<h2 className="text-lg font-bold">Link Berita</h2>
|
<h2 className="text-lg font-bold">{t("news-links")}</h2>
|
||||||
{urlInputs.map((url: any, index: any) => (
|
{urlInputs.map((url: any, index: any) => (
|
||||||
<div
|
<div
|
||||||
key={url.id}
|
key={url.id}
|
||||||
|
|
@ -1066,7 +1137,7 @@ export default function FormTaskEdit() {
|
||||||
className="mt-4 bg-green-500 text-white px-4 py-2 rounded"
|
className="mt-4 bg-green-500 text-white px-4 py-2 rounded"
|
||||||
onClick={handleAddLink}
|
onClick={handleAddLink}
|
||||||
>
|
>
|
||||||
Tambah Link
|
{t("add-links")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,15 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
import { ChevronDown, ChevronUp, Trash2 } from "lucide-react";
|
||||||
import { AudioRecorder } from "react-audio-voice-recorder";
|
import { AudioRecorder } from "react-audio-voice-recorder";
|
||||||
import FileUploader from "@/components/form/shared/file-uploader";
|
import FileUploader from "@/components/form/shared/file-uploader";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
import { error } from "@/config/swal";
|
import { error } from "@/config/swal";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
import { loading } from "@/lib/swal";
|
import { loading } from "@/lib/swal";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -73,6 +75,13 @@ export type taskDetail = {
|
||||||
is_active: string;
|
is_active: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
export default function FormTask() {
|
export default function FormTask() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -107,6 +116,7 @@ export default function FormTask() {
|
||||||
const [isRecording, setIsRecording] = useState(false);
|
const [isRecording, setIsRecording] = useState(false);
|
||||||
const [timer, setTimer] = useState<number>(120);
|
const [timer, setTimer] = useState<number>(120);
|
||||||
|
|
||||||
|
const t = useTranslations("Form");
|
||||||
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
||||||
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
|
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
|
||||||
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
|
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
|
||||||
|
|
@ -186,6 +196,63 @@ export default function FormTask() {
|
||||||
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUnitChange = (
|
||||||
|
key: keyof typeof unitSelection,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "allUnit") {
|
||||||
|
const newState = {
|
||||||
|
allUnit: value,
|
||||||
|
mabes: value,
|
||||||
|
polda: value,
|
||||||
|
polres: value,
|
||||||
|
satker: value,
|
||||||
|
};
|
||||||
|
setUnitSelection(newState);
|
||||||
|
} else {
|
||||||
|
const updatedSelection = {
|
||||||
|
...unitSelection,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["mabes", "polda", "polres", "satker"].every(
|
||||||
|
(k) => updatedSelection[k as keyof typeof unitSelection]
|
||||||
|
);
|
||||||
|
|
||||||
|
updatedSelection.allUnit = allChecked;
|
||||||
|
|
||||||
|
setUnitSelection(updatedSelection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTaskOutputChange = (
|
||||||
|
key: keyof typeof taskOutput,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
if (key === "all") {
|
||||||
|
const newState = {
|
||||||
|
all: value,
|
||||||
|
video: value,
|
||||||
|
audio: value,
|
||||||
|
image: value,
|
||||||
|
text: value,
|
||||||
|
};
|
||||||
|
setTaskOutput(newState);
|
||||||
|
} else {
|
||||||
|
const updated = {
|
||||||
|
...taskOutput,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allChecked = ["video", "audio", "image", "text"].every(
|
||||||
|
(k) => updated[k as keyof typeof taskOutput]
|
||||||
|
);
|
||||||
|
|
||||||
|
updated.all = allChecked;
|
||||||
|
setTaskOutput(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const save = async (data: TaskSchema) => {
|
const save = async (data: TaskSchema) => {
|
||||||
const fileTypeMapping = {
|
const fileTypeMapping = {
|
||||||
all: "1",
|
all: "1",
|
||||||
|
|
@ -478,13 +545,13 @@ export default function FormTask() {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="px-6 py-6">
|
<div className="px-6 py-6">
|
||||||
<p className="text-lg font-semibold mb-3">Form Penugasan</p>
|
<p className="text-lg font-semibold mb-3">{t("form-task")}</p>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Judul</Label>
|
<Label>{t("title")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="title"
|
name="title"
|
||||||
|
|
@ -503,11 +570,11 @@ export default function FormTask() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Tujuan Pemilihan Tugas</Label>
|
<Label>{t("assignment-selection")}</Label>
|
||||||
<Select onValueChange={setSelectedTarget}>
|
<Select onValueChange={setSelectedTarget}>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Choose" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="3,4">Semua Pengguna</SelectItem>
|
<SelectItem value="3,4">Semua Pengguna</SelectItem>
|
||||||
|
|
@ -516,14 +583,17 @@ export default function FormTask() {
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-3 mt-5 lg:pt-5 lg:ml-3">
|
<div className="flex flex-wrap gap-3 mt-5 lg:pt-7 lg:ml-3 ">
|
||||||
{Object.keys(unitSelection).map((key) => (
|
{Object.keys(unitSelection).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={key}
|
id={key}
|
||||||
checked={unitSelection[key as keyof typeof unitSelection]}
|
checked={unitSelection[key as keyof typeof unitSelection]}
|
||||||
onCheckedChange={(value) =>
|
onCheckedChange={(value) =>
|
||||||
setUnitSelection({ ...unitSelection, [key]: value })
|
handleUnitChange(
|
||||||
|
key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={key}>
|
<Label htmlFor={key}>
|
||||||
|
|
@ -532,11 +602,11 @@ export default function FormTask() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 lg:pt-5 lg:pl-3">
|
<div className="mt-6 lg:pt-6 lg:pl-3">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="soft" size="sm" color="primary">
|
<Button variant="soft" size="sm" color="primary">
|
||||||
[Kustom]
|
[{t("custom")}]
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||||
|
|
@ -614,10 +684,10 @@ export default function FormTask() {
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Tipe Penugasan</Label>
|
<Label>{t("type-task")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={mainType} // State yang dipetakan ke value RadioGroup
|
value={mainType}
|
||||||
onValueChange={(value) => setMainType(value)}
|
onValueChange={(value) => setMainType(value)}
|
||||||
// value={String(mainType)}
|
// value={String(mainType)}
|
||||||
// onValueChange={(value) => setMainType(Number(value))}
|
// onValueChange={(value) => setMainType(Number(value))}
|
||||||
|
|
@ -629,8 +699,8 @@ export default function FormTask() {
|
||||||
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Jenis Tugas </Label>
|
<Label>{t("assigment-type")} </Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={taskType}
|
value={taskType}
|
||||||
onValueChange={(value) => setTaskType(String(value))}
|
onValueChange={(value) => setTaskType(String(value))}
|
||||||
|
|
@ -643,8 +713,8 @@ export default function FormTask() {
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
{/* RadioGroup Assignment Category */}
|
{/* RadioGroup Assignment Category */}
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>{t("type-of-task")}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={type} // State yang dipetakan ke value RadioGroup
|
value={type} // State yang dipetakan ke value RadioGroup
|
||||||
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
||||||
|
|
@ -664,16 +734,19 @@ export default function FormTask() {
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Output Tugas</Label>
|
<Label>{t("output-task")}</Label>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-4">
|
||||||
{Object.keys(taskOutput).map((key) => (
|
{Object.keys(taskOutput).map((key) => (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={key}
|
id={key}
|
||||||
checked={taskOutput[key as keyof typeof taskOutput]}
|
checked={taskOutput[key as keyof typeof taskOutput]}
|
||||||
onCheckedChange={(value) =>
|
onCheckedChange={(value) =>
|
||||||
setTaskOutput({ ...taskOutput, [key]: value })
|
handleTaskOutputChange(
|
||||||
|
key as keyof typeof taskOutput,
|
||||||
|
value as boolean
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={key}>
|
<Label htmlFor={key}>
|
||||||
|
|
@ -704,18 +777,13 @@ export default function FormTask() {
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="mt-5">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>Narasi Penugasan</Label>
|
<Label>{t("description")}</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="naration"
|
name="naration"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
ref={editor}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className="dark:text-black"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors.naration?.message && (
|
{errors.naration?.message && (
|
||||||
|
|
@ -724,11 +792,11 @@ export default function FormTask() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 mt-5">
|
<div className="space-y-2.5 mt-5">
|
||||||
<Label htmlFor="attachments">Lampiran</Label>
|
<Label htmlFor="attachments">{t("attachment")}</Label>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<Label>Video</Label>
|
<Label>{t("audio-visual")}</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"mp4/*": [],
|
"mp4/*": [],
|
||||||
|
|
@ -739,8 +807,8 @@ export default function FormTask() {
|
||||||
onDrop={(files) => setVideoFiles(files)}
|
onDrop={(files) => setVideoFiles(files)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Foto</Label>
|
<Label>{t("image")}</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"image/*": [],
|
"image/*": [],
|
||||||
|
|
@ -750,8 +818,8 @@ export default function FormTask() {
|
||||||
onDrop={(files) => setImageFiles(files)}
|
onDrop={(files) => setImageFiles(files)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Teks</Label>
|
<Label>{t("text")}</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"pdf/*": [],
|
"pdf/*": [],
|
||||||
|
|
@ -761,8 +829,8 @@ export default function FormTask() {
|
||||||
onDrop={(files) => setTextFiles(files)}
|
onDrop={(files) => setTextFiles(files)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<Label>Audio</Label>
|
<Label>{t("audio")}</Label>
|
||||||
<AudioRecorder
|
<AudioRecorder
|
||||||
onRecordingComplete={addAudioElement}
|
onRecordingComplete={addAudioElement}
|
||||||
audioTrackConstraints={{
|
audioTrackConstraints={{
|
||||||
|
|
@ -790,7 +858,7 @@ export default function FormTask() {
|
||||||
key={idx}
|
key={idx}
|
||||||
className="flex flex-row justify-between items-center"
|
className="flex flex-row justify-between items-center"
|
||||||
>
|
>
|
||||||
<p>Voice Note</p>
|
<p>{t("voice-note")}</p>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleDeleteAudio(idx)}
|
onClick={() => handleDeleteAudio(idx)}
|
||||||
|
|
@ -803,11 +871,11 @@ export default function FormTask() {
|
||||||
))}
|
))}
|
||||||
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||||
{/* Display remaining time */}
|
{/* Display remaining time */}
|
||||||
<div className="mt-4">
|
<div className="mt-4 space-y-2">
|
||||||
<h2 className="text-lg font-bold">Link Berita</h2>
|
<Label className="">{t("news-links")}</Label>
|
||||||
{links.map((link, index) => (
|
{links.map((link, index) => (
|
||||||
<div key={index} className="flex items-center gap-2 mt-2">
|
<div key={index} className="flex items-center gap-2 mt-2">
|
||||||
<input
|
<Input
|
||||||
type="url"
|
type="url"
|
||||||
className="border rounded p-2 w-full"
|
className="border rounded p-2 w-full"
|
||||||
placeholder={`Masukkan link berita ${index + 1}`}
|
placeholder={`Masukkan link berita ${index + 1}`}
|
||||||
|
|
@ -822,18 +890,19 @@ export default function FormTask() {
|
||||||
className="bg-red-500 text-white px-3 py-1 rounded"
|
className="bg-red-500 text-white px-3 py-1 rounded"
|
||||||
onClick={() => handleRemoveRow(index)}
|
onClick={() => handleRemoveRow(index)}
|
||||||
>
|
>
|
||||||
Hapus
|
<Trash2 className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
|
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
|
||||||
onClick={handleAddRow}
|
onClick={handleAddRow}
|
||||||
|
size="sm"
|
||||||
>
|
>
|
||||||
Tambah Link
|
{t("add-links")}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -842,7 +911,7 @@ export default function FormTask() {
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
{t("submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import { getCategoryData, getPublicCategoryData } from "@/service/landing/landing";
|
import {
|
||||||
|
getCategoryData,
|
||||||
|
getPublicCategoryData,
|
||||||
|
} from "@/service/landing/landing";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
@ -21,7 +24,15 @@ const ContentCategory = (props: { group?: string }) => {
|
||||||
}, []);
|
}, []);
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const response = await getPublicCategoryData(
|
const response = await getPublicCategoryData(
|
||||||
props.group == "mabes" ? "" : props.group == "polda" && poldaName && String(poldaName)?.length > 1 ? poldaName : props.group == "satker" && satkerName && String(satkerName)?.length > 1 ? "satker-" + satkerName : "",
|
props.group == "mabes"
|
||||||
|
? ""
|
||||||
|
: props.group == "polda" && poldaName && String(poldaName)?.length > 1
|
||||||
|
? poldaName
|
||||||
|
: props.group == "satker" &&
|
||||||
|
satkerName &&
|
||||||
|
String(satkerName)?.length > 1
|
||||||
|
? "satker-" + satkerName
|
||||||
|
: "",
|
||||||
"",
|
"",
|
||||||
locale == "en" ? true : false
|
locale == "en" ? true : false
|
||||||
);
|
);
|
||||||
|
|
@ -47,7 +58,10 @@ const ContentCategory = (props: { group?: string }) => {
|
||||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
const toBase64 = (str: string) =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? Buffer.from(str).toString("base64")
|
||||||
|
: window.btoa(str);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-4 lg:px-24 py-10">
|
<div className="px-4 lg:px-24 py-10">
|
||||||
|
|
@ -84,10 +98,16 @@ const ContentCategory = (props: { group?: string }) => {
|
||||||
// <h3 className="text-sm font-semibold truncate">{category?.name}</h3>
|
// <h3 className="text-sm font-semibold truncate">{category?.name}</h3>
|
||||||
// </div>
|
// </div>
|
||||||
// </Link>
|
// </Link>
|
||||||
<Link key={category?.id} href={`all/filter?category=${category?.id}`} className="relative group rounded-md overflow-hidden shadow-md hover:shadow-lg">
|
<Link
|
||||||
|
key={category?.id}
|
||||||
|
href={`all/filter?category=${category?.id}`}
|
||||||
|
className="relative group rounded-md overflow-hidden shadow-md hover:shadow-lg"
|
||||||
|
>
|
||||||
{/* Gambar */}
|
{/* Gambar */}
|
||||||
<Image
|
<Image
|
||||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
alt="category"
|
alt="category"
|
||||||
width={2560}
|
width={2560}
|
||||||
height={1440}
|
height={1440}
|
||||||
|
|
@ -99,8 +119,10 @@ const ContentCategory = (props: { group?: string }) => {
|
||||||
<div className="absolute inset-0 bg-black bg-opacity-25 group-hover:bg-opacity-35 transition-all duration-300 rounded-md"></div>
|
<div className="absolute inset-0 bg-black bg-opacity-25 group-hover:bg-opacity-35 transition-all duration-300 rounded-md"></div>
|
||||||
|
|
||||||
{/* Judul */}
|
{/* Judul */}
|
||||||
<div className="absolute bottom-5 left-0 right-16 bg-transparent backdrop-blur-md text-white p-4 border-l-2 border-[#bb3523] z-10">
|
<div className="absolute bottom-5 left-0 right-16 bg-transparent backdrop-blur-md text-white p-4 border-l-2 border-[#bb3523] z-10 group-hover:scale-x-150 origin-left">
|
||||||
<h3 className="text-sm font-semibold truncate">{category?.name}</h3>
|
<h3 className="text-sm font-semibold truncate">
|
||||||
|
{category?.name}
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -120,10 +142,16 @@ const ContentCategory = (props: { group?: string }) => {
|
||||||
// <h3 className="text-sm font-semibold truncate">{category?.name}</h3>
|
// <h3 className="text-sm font-semibold truncate">{category?.name}</h3>
|
||||||
// </div>
|
// </div>
|
||||||
// </Link>
|
// </Link>
|
||||||
<Link key={category?.id} href={`all/filter?category=${category?.id}`} className="relative group rounded-md overflow-hidden shadow-md hover:shadow-lg">
|
<Link
|
||||||
|
key={category?.id}
|
||||||
|
href={`all/filter?category=${category?.id}`}
|
||||||
|
className="relative group rounded-md overflow-hidden shadow-md hover:shadow-lg"
|
||||||
|
>
|
||||||
{/* Gambar */}
|
{/* Gambar */}
|
||||||
<Image
|
<Image
|
||||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
alt="category"
|
alt="category"
|
||||||
width={2560}
|
width={2560}
|
||||||
height={1440}
|
height={1440}
|
||||||
|
|
@ -136,14 +164,19 @@ const ContentCategory = (props: { group?: string }) => {
|
||||||
|
|
||||||
{/* Judul */}
|
{/* Judul */}
|
||||||
<div className="absolute bottom-5 left-0 right-16 bg-transparent backdrop-blur-md text-white p-4 border-l-2 border-[#bb3523] z-10">
|
<div className="absolute bottom-5 left-0 right-16 bg-transparent backdrop-blur-md text-white p-4 border-l-2 border-[#bb3523] z-10">
|
||||||
<h3 className="text-sm font-semibold truncate">{category?.name}</h3>
|
<h3 className="text-sm font-semibold truncate">
|
||||||
|
{category?.name}
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center flex-row justify-center">
|
<div className="flex items-center flex-row justify-center">
|
||||||
<Button onClick={() => setSeeAllValue(!seeAllValue)} className="bg-white hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]">
|
<Button
|
||||||
|
onClick={() => setSeeAllValue(!seeAllValue)}
|
||||||
|
className="bg-white hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]"
|
||||||
|
>
|
||||||
{seeAllValue ? t("seeLess") : t("seeMore")}
|
{seeAllValue ? t("seeLess") : t("seeMore")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,33 @@ const Footer = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="bg-[#bb3523] text-white text-xs lg:text-sm py-4 space-y-3 lg:space-y-0 h-">
|
<footer className="bg-[#bb3523] text-white text-xs lg:text-sm py-4 space-y-3 lg:space-y-0 h-">
|
||||||
<div className="mx-auto flex flex-col md:flex-row justify-between items-center px-4">
|
<div className="mx-auto flex flex-col md:flex-row justify-between items-center px-4 gap-3">
|
||||||
{/* Hak Cipta */}
|
{/* Hak Cipta */}
|
||||||
<div className="text-center md:text-left">
|
<div className="text-center md:text-left">
|
||||||
{t("copyright")} © {new Date().getFullYear()} {t("publicRelation")} {t("reserved")}
|
{t("copyright")} © {new Date().getFullYear()}{" "}
|
||||||
|
{t("publicRelation")} {t("reserved")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Menu Links */}
|
{/* Menu Links */}
|
||||||
<div className="flex flex-wrap justify-center items-center space-x-3">
|
<div className="flex flex-wrap justify-center items-center space-x-3">
|
||||||
<Link href={generateLocalizedPath("/feedback", String(locale))} className="hover:underline">
|
<Link
|
||||||
|
href={generateLocalizedPath("/feedback", String(locale))}
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
{t("feedback")}
|
{t("feedback")}
|
||||||
</Link>
|
</Link>
|
||||||
<span className="hidden md:inline-block ">|</span>
|
<span className="hidden md:inline-block ">|</span>
|
||||||
<Link href={generateLocalizedPath("/contact", String(locale))} className="hover:underline">
|
<Link
|
||||||
|
href={generateLocalizedPath("/contact", String(locale))}
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
{t("contact")}
|
{t("contact")}
|
||||||
</Link>
|
</Link>
|
||||||
<span className="hidden md:inline-block">|</span>
|
<span className="hidden md:inline-block">|</span>
|
||||||
<Link href={generateLocalizedPath("/faqs", String(locale))} className="hover:underline">
|
<Link
|
||||||
|
href={generateLocalizedPath("/faqs", String(locale))}
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
FAQ
|
FAQ
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
@ -55,12 +65,17 @@ const Footer = () => {
|
||||||
{t("privacy")}
|
{t("privacy")}
|
||||||
</a>
|
</a>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="flex flex-col overflow-y-scroll h-[80%]" size="md">
|
<DialogContent
|
||||||
|
className="flex flex-col overflow-y-scroll h-[80%]"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
<div className="flex flex-row items-center justify-center gap-4">
|
<div className="flex flex-row items-center justify-center gap-4">
|
||||||
<img src="/assets/icon-privacy.png" alt="Privacy" />
|
<img src="/assets/icon-privacy.png" alt="Privacy" />
|
||||||
<p className="font-semibold text-lg">{t("privacy")}</p>
|
<p className="font-semibold text-lg">{t("privacy")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="container text-black dark:text-white space-y-2">{parse(String(privacy))}</div>
|
<div className="container text-black dark:text-white space-y-2">
|
||||||
|
{parse(String(privacy))}
|
||||||
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -69,13 +84,25 @@ const Footer = () => {
|
||||||
<div className="flex justify-center items-center space-x-3">
|
<div className="flex justify-center items-center space-x-3">
|
||||||
<span className="text-sm">Follow Us:</span>
|
<span className="text-sm">Follow Us:</span>
|
||||||
<a href="#" aria-label="Facebook">
|
<a href="#" aria-label="Facebook">
|
||||||
<img src="/assets/facebook.svg" alt="Facebook" className="w-5 h-5" />
|
<img
|
||||||
|
src="/assets/facebook.svg"
|
||||||
|
alt="Facebook"
|
||||||
|
className="w-5 h-5"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" aria-label="Instagram">
|
<a href="#" aria-label="Instagram">
|
||||||
<img src="/assets/instagram.svg" alt="Instagram" className="w-5 h-5" />
|
<img
|
||||||
|
src="/assets/instagram.svg"
|
||||||
|
alt="Instagram"
|
||||||
|
className="w-5 h-5"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" aria-label="Twitter">
|
<a href="#" aria-label="Twitter">
|
||||||
<img src="/assets/twitter.svg" alt="Instagram" className="w-5 h-5" />
|
<img
|
||||||
|
src="/assets/twitter.svg"
|
||||||
|
alt="Instagram"
|
||||||
|
className="w-5 h-5"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" aria-label="TikTok">
|
<a href="#" aria-label="TikTok">
|
||||||
<img src="/assets/tiktok.svg" alt="TikTok" className="w-5 h-5" />
|
<img src="/assets/tiktok.svg" alt="TikTok" className="w-5 h-5" />
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,372 @@ import "swiper/css/navigation";
|
||||||
import { getHeroData } from "@/service/landing/landing";
|
import { getHeroData } from "@/service/landing/landing";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams, usePathname, useRouter } from "next/navigation";
|
import { useParams, usePathname, useRouter } from "next/navigation";
|
||||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/components/ui/carousel";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "../ui/card";
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { Textarea } from "../ui/textarea";
|
||||||
|
import { Checkbox } from "../ui/checkbox";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "../ui/dialog";
|
||||||
|
import { Autoplay, Pagination } from "swiper/modules";
|
||||||
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
|
import "swiper/css";
|
||||||
|
import "swiper/css/pagination";
|
||||||
|
|
||||||
|
const HeroModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
|
const [heroData, setHeroData] = useState<any>();
|
||||||
|
const params = useParams();
|
||||||
|
const locale = params?.locale;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchCategories() {
|
||||||
|
const url = "https://netidhub.com/api/csrf";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fetch error: ", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchCategories();
|
||||||
|
initFetch();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initFetch = async () => {
|
||||||
|
const response = await getHeroData();
|
||||||
|
console.log(response);
|
||||||
|
setHeroData(response?.data?.data?.content);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center backdrop-brightness-50 z-50">
|
||||||
|
<div className="relative dark:bg-gray-900 rounded-lg w-[90%] md:w-[600px] p-4 shadow-none">
|
||||||
|
<Swiper
|
||||||
|
pagination={{ dynamicBullets: true }}
|
||||||
|
modules={[Pagination, Autoplay]}
|
||||||
|
className="mySwiper w-full"
|
||||||
|
>
|
||||||
|
{heroData?.map((list: any, index: number) => (
|
||||||
|
<SwiperSlide key={list?.id}>
|
||||||
|
<div className="relative h-[310px] lg:h-[420px]">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute top-3 right-3 text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-white"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
<Image
|
||||||
|
src={list?.thumbnailLink}
|
||||||
|
alt="gambar-utama"
|
||||||
|
width={1920}
|
||||||
|
height={1080}
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
className="w-full h-[310px] lg:h-[420px] rounded-lg object-cover"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 bg-black/30 backdrop-brightness-50 text-white pb-4 px-4 pt-8 rounded-bl-2xl rounded-tr-2xl mx-3 mb-2">
|
||||||
|
<div className="absolute top-0 left-0 bottom-0 w-2 bg-[#bb3523] rounded-bl-lg"></div>
|
||||||
|
<span className="absolute top-0 left-0 mt-2 mb-3 mx-3 bg-[#bb3523] text-white text-xs font-semibold uppercase px-2 py-1 rounded">
|
||||||
|
{list?.categoryName || "Liputan Kegiatan"}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<Link href={`${locale}/image/detail/${list?.slug}`}>
|
||||||
|
<h2 className="text-lg leading-tight">{list?.title}</h2>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<p className="text-xs flex items-center gap-1 mt-2 opacity-80">
|
||||||
|
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
||||||
|
{list?.timezone || "WIB"} |{" "}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
className="inline-block"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{list?.clickCount}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
<style jsx global>{`
|
||||||
|
.swiper-pagination-bullet {
|
||||||
|
background: white !important;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.swiper-pagination-bullet-active {
|
||||||
|
background: white !important;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SurveyIntroModal = ({ onNext }: { onNext: () => void }) => {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||||
|
<div className="relative bg-white rounded-xl p-6 w-[90%] max-w-md text-center">
|
||||||
|
<button
|
||||||
|
onClick={onNext}
|
||||||
|
className="absolute top-3 right-3 text-gray-500 hover:text-black"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src="/assets/survey.jpg"
|
||||||
|
alt="Survey Illustration"
|
||||||
|
width={300}
|
||||||
|
height={200}
|
||||||
|
className="mx-auto my-4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={onNext}
|
||||||
|
className="mt-4 bg-red-600 hover:bg-red-700 text-white font-semibold py-3 px-6 rounded w-full"
|
||||||
|
>
|
||||||
|
Lihat Selengkapnya
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
q1: [
|
||||||
|
"Setiap hari",
|
||||||
|
"Beberapa kali seminggu",
|
||||||
|
"Beberapa kali dalam sebulan",
|
||||||
|
"Baru pertama kali",
|
||||||
|
],
|
||||||
|
q2a: ["Sangat baik", "Baik", "Cukup", "Kurang", "Buruk"],
|
||||||
|
q2b: ["Sangat mudah", "Mudah", "Cukup", "Sulit", "Sangat sulit"],
|
||||||
|
q2c: ["Sangat cepat", "Cepat", "Cukup", "Lambat", "Sangat lambat"],
|
||||||
|
q3a: ["Sangat puas", "Puas", "Cukup", "Kurang puas", "Tidak puas"],
|
||||||
|
q3b: [
|
||||||
|
"Sangat lengkap",
|
||||||
|
"Lengkap",
|
||||||
|
"Cukup",
|
||||||
|
"Kurang lengkap",
|
||||||
|
"Tidak lengkap",
|
||||||
|
],
|
||||||
|
q4: [
|
||||||
|
"Sangat membantu",
|
||||||
|
"Membantu",
|
||||||
|
"Cukup membantu",
|
||||||
|
"Kurang membantu",
|
||||||
|
"Tidak membantu",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const SurveyFormModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = "";
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent className="z-50 min-w-max h-[600px] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-lg font-bold">
|
||||||
|
SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription className="text-sm">
|
||||||
|
Kami menghargai pendapat Anda! Survei ini bertujuan untuk
|
||||||
|
meningkatkan kualitas layanan MediaHub Polri. Mohon luangkan waktu
|
||||||
|
beberapa menit untuk mengisi survei ini.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-4 mt-4">
|
||||||
|
{/* 1 */}
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">
|
||||||
|
1. Seberapa sering Anda mengakses MediaHub Polri?
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||||
|
{options.q1.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q1-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 2 */}
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">
|
||||||
|
2. Bagaimana pengalaman Anda dalam mengakses website ini?
|
||||||
|
</p>
|
||||||
|
<div className="mt-2 space-y-3">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
a) Tampilan dan desain website
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-2 mt-1">
|
||||||
|
{options.q2a.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q2a-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
b) Kemudahan navigasi (pencarian informasi, menu, dll)
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-2 mt-1">
|
||||||
|
{options.q2b.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q2b-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
c) Kecepatan akses website
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-2 mt-1">
|
||||||
|
{options.q2c.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q2c-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 3 */}
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">
|
||||||
|
3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub
|
||||||
|
Polri?
|
||||||
|
</p>
|
||||||
|
<div className="mt-2 space-y-3">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium">a) Akurat dan terpercaya</p>
|
||||||
|
<div className="grid grid-cols-3 gap-2 mt-1">
|
||||||
|
{options.q3a.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q3a-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
b) Kelengkapan berita dan informasi
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-2 mt-1">
|
||||||
|
{options.q3b.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q3b-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 4 */}
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">
|
||||||
|
5. Apakah Anda merasa website ini membantu dalam mendapatkan
|
||||||
|
informasi terkait Polri?
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||||
|
{options.q4.map((item, i) => (
|
||||||
|
<label key={i} className="flex items-center space-x-2">
|
||||||
|
<Checkbox id={`q4-${i}`} />
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 5 */}
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">
|
||||||
|
6. Apa saran atau masukan Anda untuk meningkatkan layanan MediaHub
|
||||||
|
Polri?
|
||||||
|
</p>
|
||||||
|
<Textarea placeholder="Tulis pesan Anda" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-2 mt-6">
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline">Batal</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button>Kirim</Button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ONE_MONTH = 30 * 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
const Hero: React.FC = () => {
|
const Hero: React.FC = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -16,6 +379,9 @@ const Hero: React.FC = () => {
|
||||||
const locale = params?.locale;
|
const locale = params?.locale;
|
||||||
const [isLoading, setIsLoading] = useState<any>(true);
|
const [isLoading, setIsLoading] = useState<any>(true);
|
||||||
const [heroData, setHeroData] = useState<any>();
|
const [heroData, setHeroData] = useState<any>();
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [showSurveyModal, setShowSurveyModal] = useState(false);
|
||||||
|
const [showFormModal, setShowFormModal] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
|
|
@ -25,6 +391,29 @@ const Hero: React.FC = () => {
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const roleId = Cookies.get("urie");
|
||||||
|
if (!roleId) {
|
||||||
|
setShowModal(true);
|
||||||
|
}
|
||||||
|
initFetch();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const roleId = Cookies.get("urie");
|
||||||
|
const lastShown = Cookies.get("surveyLastShown");
|
||||||
|
const now = new Date().getTime();
|
||||||
|
|
||||||
|
if (roleId && roleId !== "2") {
|
||||||
|
if (!lastShown || now - parseInt(lastShown) > ONE_MONTH) {
|
||||||
|
setShowSurveyModal(true);
|
||||||
|
Cookies.set("surveyLastShown", now.toString(), { expires: 30 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initFetch();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchCategories() {
|
async function fetchCategories() {
|
||||||
const url = "https://netidhub.com/api/csrf";
|
const url = "https://netidhub.com/api/csrf";
|
||||||
|
|
@ -37,7 +426,7 @@ const Hero: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data; // Menampilkan data yang diterima dari API
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Fetch error: ", error);
|
console.error("Fetch error: ", error);
|
||||||
}
|
}
|
||||||
|
|
@ -67,11 +456,28 @@ const Hero: React.FC = () => {
|
||||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
const toBase64 = (str: string) =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? Buffer.from(str).toString("base64")
|
||||||
|
: window.btoa(str);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col lg:flex-row items-start justify-center gap-8 px-4 lg:px-20 py-4 mx-auto w-auto mt-6">
|
<div className="flex flex-col lg:flex-row items-start justify-center gap-8 px-4 lg:px-20 py-4 mx-auto w-auto mt-6">
|
||||||
{/* Section Gambar Utama */}
|
<div className="relative">
|
||||||
|
{showModal && <HeroModal onClose={() => setShowModal(false)} />}
|
||||||
|
{showSurveyModal && !showFormModal && (
|
||||||
|
<SurveyIntroModal
|
||||||
|
onNext={() => {
|
||||||
|
setShowSurveyModal(false);
|
||||||
|
setShowFormModal(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showFormModal && (
|
||||||
|
<SurveyFormModal onClose={() => setShowFormModal(false)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex flex-col space-y-3 mx-auto w-full lg:w-2/3">
|
<div className="flex flex-col space-y-3 mx-auto w-full lg:w-2/3">
|
||||||
<Skeleton className="h-[310px] lg:h-[420px] rounded-xl" />
|
<Skeleton className="h-[310px] lg:h-[420px] rounded-xl" />
|
||||||
|
|
@ -85,30 +491,44 @@ const Hero: React.FC = () => {
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
{heroData?.map((list: any) => (
|
{heroData?.map((list: any) => (
|
||||||
<CarouselItem key={list?.id}>
|
<CarouselItem key={list?.id}>
|
||||||
<div className="relative h-[310px] lg:h-[420px]">
|
<div className="relative h-[310px] lg:h-[460px] mt-1">
|
||||||
<Image
|
<Image
|
||||||
src={list?.thumbnailLink}
|
src={list?.thumbnailLink}
|
||||||
alt="gambar-utama"
|
alt="gambar-utama"
|
||||||
width={1920}
|
width={1920}
|
||||||
height={1080}
|
height={1080}
|
||||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
className="w-full h-[310px] lg:h-[420px] rounded-lg object-cover"
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
className="w-full h-[320px] lg:h-[460px] rounded-lg object-cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-black dark:text-white p-4 rounded-b-lg">
|
<div className="absolute bottom-0 left-0 right-0 bg-black/30 backdrop-brightness-50 text-white pb-4 px-4 pt-8 rounded-bl-2xl rounded-tr-2xl mx-3 mb-2">
|
||||||
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-xs px-2 py-1">{list?.categoryName}</span>
|
<div className="absolute top-0 left-0 bottom-0 w-2 bg-[#bb3523] rounded-bl-lg"></div>
|
||||||
|
<span className="absolute top-0 left-0 mt-2 mb-3 mx-3 bg-[#bb3523] text-white text-xs font-semibold uppercase px-2 py-1 rounded">
|
||||||
|
{list?.categoryName || "Liputan Kegiatan"}
|
||||||
|
</span>
|
||||||
|
|
||||||
<Link href={`${locale}/image/detail/${list?.slug}`}>
|
<Link href={`${locale}/image/detail/${list?.slug}`}>
|
||||||
<h2 className="text-lg text-slate-500 dark:text-white font-bold mt-2">{list?.title}</h2>
|
<h2 className="text-lg leading-tight">{list?.title}</h2>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-xs flex flex-row items-center text-slate-500 dark:text-white gap-1 mt-1">
|
|
||||||
{formatDateToIndonesian(new Date(list?.createdAt))} {list?.timezone ? list?.timezone : "WIB"}|{" "}
|
<p className="text-xs flex items-center gap-1 mt-2 opacity-80">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
|
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
||||||
|
{list?.timezone || "WIB"} |{" "}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
className="inline-block"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
/>
|
/>
|
||||||
</svg>{" "}
|
</svg>
|
||||||
{list?.clickCount}{" "}
|
{list?.clickCount}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -120,7 +540,6 @@ const Hero: React.FC = () => {
|
||||||
</Carousel>
|
</Carousel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Section Kanan */}
|
|
||||||
<div>
|
<div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -161,30 +580,157 @@ const Hero: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ul className="py-4 lg:py-0 flex flex-row lg:flex-col gap-4 flex-nowrap w-[95vw] lg:w-auto overflow-x-auto">
|
<ul className="py-4 lg:py-0 flex flex-row lg:flex-col gap-4 flex-nowrap w-[95vw] lg:w-auto">
|
||||||
{heroData?.map((item: any) => (
|
<Tabs defaultValue="national" className="w-[350px]">
|
||||||
<li key={item?.id} className="flex gap-4 flex-row lg:w-full ">
|
<TabsList className="grid w-full grid-cols-3 border">
|
||||||
<div className="flex-shrink-0 w-24 rounded-lg">
|
<TabsTrigger value="national">Nasional</TabsTrigger>
|
||||||
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={720} height={480} src={item?.thumbnailLink} alt={item?.title} className="w-full h-[73px] object-cover rounded-lg" />
|
<TabsTrigger value="polda">Polda</TabsTrigger>
|
||||||
</div>
|
<TabsTrigger value="satker">Satker</TabsTrigger>
|
||||||
<div className="w-[280px] lg:w-auto">
|
</TabsList>
|
||||||
<span className="text-white bg-[#bb3523] px-4 py-1 rounded-lg flex text-[8px] font-bold uppercase w-fit">{item?.categoryName}</span>
|
<TabsContent value="national">
|
||||||
<Link href={`${locale}/image/detail/${item?.slug}`}>
|
{heroData?.map((item: any) => (
|
||||||
<h3 className="text-base font-bold mt-2 h-6 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{item?.title}</h3>
|
<li
|
||||||
</Link>
|
key={item?.id}
|
||||||
<p className="text-[10px] flex flex-row items-center gap-1 text-gray-500 mt-1">
|
className="flex gap-4 flex-row lg:w-full mx-2"
|
||||||
{formatDateToIndonesian(new Date(item?.createdAt))} {item?.timezone ? item?.timezone : "WIB"} |{" "}
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
|
<div className="flex-shrink-0 w-24 rounded-lg">
|
||||||
<path
|
<Image
|
||||||
fill="currentColor"
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={720}
|
||||||
|
height={480}
|
||||||
|
src={item?.thumbnailLink}
|
||||||
|
alt={item?.title}
|
||||||
|
className="w-full h-[73px] object-cover rounded-lg"
|
||||||
/>
|
/>
|
||||||
</svg>{" "}
|
</div>
|
||||||
{item?.clickCount}
|
<div className="w-[280px] lg:w-auto">
|
||||||
</p>
|
<span className="text-white bg-[#bb3523] px-4 py-1 rounded-lg flex text-[8px] font-bold uppercase w-fit">
|
||||||
</div>
|
{item?.categoryName}
|
||||||
</li>
|
</span>
|
||||||
))}
|
<Link href={`${locale}/image/detail/${item?.slug}`}>
|
||||||
|
<h3 className="text-base font-bold mt-2 h-6 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
|
||||||
|
{item?.title}
|
||||||
|
</h3>
|
||||||
|
</Link>
|
||||||
|
<p className="text-[10px] flex flex-row items-center gap-1 text-gray-500 mt-1">
|
||||||
|
{formatDateToIndonesian(new Date(item?.createdAt))}{" "}
|
||||||
|
{item?.timezone ? item?.timezone : "WIB"} |{" "}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
|
/>
|
||||||
|
</svg>{" "}
|
||||||
|
{item?.clickCount}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="polda">
|
||||||
|
{heroData
|
||||||
|
?.filter((item: any) => item.isPublishOnPolda === true)
|
||||||
|
.map((item: any, index: any) => (
|
||||||
|
<li
|
||||||
|
key={item?.id}
|
||||||
|
className="flex gap-4 flex-row lg:w-full mx-2"
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 w-24 rounded-lg">
|
||||||
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={720}
|
||||||
|
height={480}
|
||||||
|
src={item?.thumbnailLink}
|
||||||
|
alt={item?.title}
|
||||||
|
className="w-full h-[73px] object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-[280px] lg:w-auto">
|
||||||
|
<span className="text-white bg-[#bb3523] px-4 py-1 rounded-lg flex text-[8px] font-bold uppercase w-fit">
|
||||||
|
{item?.categoryName}
|
||||||
|
</span>
|
||||||
|
<Link href={`${locale}/image/detail/${item?.slug}`}>
|
||||||
|
<h3 className="text-base font-bold mt-2 h-6 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
|
||||||
|
{item?.title}
|
||||||
|
</h3>
|
||||||
|
</Link>
|
||||||
|
<p className="text-[10px] flex flex-row items-center gap-1 text-gray-500 mt-1">
|
||||||
|
{formatDateToIndonesian(new Date(item?.createdAt))}{" "}
|
||||||
|
{item?.timezone ? item?.timezone : "WIB"} |{" "}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
|
/>
|
||||||
|
</svg>{" "}
|
||||||
|
{item?.clickCount}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="satker">
|
||||||
|
{heroData?.map((item: any) => (
|
||||||
|
<li
|
||||||
|
key={item?.id}
|
||||||
|
className="flex gap-4 flex-row lg:w-full mx-2"
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 w-24 rounded-lg">
|
||||||
|
<Image
|
||||||
|
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||||
|
shimmer(700, 475)
|
||||||
|
)}`}
|
||||||
|
width={720}
|
||||||
|
height={480}
|
||||||
|
src={item?.thumbnailLink}
|
||||||
|
alt={item?.title}
|
||||||
|
className="w-full h-[73px] object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-[280px] lg:w-auto">
|
||||||
|
<span className="text-white bg-[#bb3523] px-4 py-1 rounded-lg flex text-[8px] font-bold uppercase w-fit">
|
||||||
|
{item?.categoryName}
|
||||||
|
</span>
|
||||||
|
<Link href={`${locale}/image/detail/${item?.slug}`}>
|
||||||
|
<h3 className="text-base font-bold mt-2 h-6 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
|
||||||
|
{item?.title}
|
||||||
|
</h3>
|
||||||
|
</Link>
|
||||||
|
<p className="text-[10px] flex flex-row items-center gap-1 text-gray-500 mt-1">
|
||||||
|
{formatDateToIndonesian(new Date(item?.createdAt))}{" "}
|
||||||
|
{item?.timezone ? item?.timezone : "WIB"} |{" "}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
|
||||||
|
/>
|
||||||
|
</svg>{" "}
|
||||||
|
{item?.clickCount}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ const HeaderBannerKaltara = () => {
|
||||||
<>
|
<>
|
||||||
<Reveal>
|
<Reveal>
|
||||||
{/* Header Left */}
|
{/* Header Left */}
|
||||||
<div className="flex flex-col lg:flex-row items-start justify-center gap-[25px] px-4 lg:px-18 py-4 w-auto mt-6">
|
<div className=" flex flex-col lg:flex-row items-start justify-center gap-[25px] px-4 lg:px-18 py-4 w-auto mt-6">
|
||||||
{isBannerLoading ? (
|
{isBannerLoading ? (
|
||||||
<div className="flex flex-col space-y-3 mx-auto w-full lg:w-2/3">
|
<div className="flex flex-col space-y-3 mx-auto w-full lg:w-2/3">
|
||||||
<Skeleton className="h-[310px] lg:h-[420px] rounded-xl" />
|
<Skeleton className="h-[310px] lg:h-[420px] rounded-xl" />
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue