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>
|
||||||
|
|
||||||
|
{showExternalButton && (
|
||||||
<Button
|
<Button
|
||||||
rounded="md"
|
rounded="md"
|
||||||
onClick={() => setIsInternal(false)}
|
onClick={() => setIsInternal(false)}
|
||||||
className={`hover:text-white ${
|
className={`hover:text-white ${
|
||||||
!isInternal ? "bg-black text-white" : "bg-white text-black"
|
!isInternal ? "bg-black text-white" : "bg-white text-black"
|
||||||
}
|
}`}
|
||||||
`}
|
|
||||||
>
|
>
|
||||||
User Eksternal
|
User Eksternal
|
||||||
</Button>
|
</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,9 +244,37 @@ 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>
|
||||||
|
<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
|
<Button
|
||||||
|
|
@ -321,7 +352,6 @@ const TaskTable = () => {
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|
|
||||||
|
|
@ -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,7 +94,7 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
@ -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">
|
||||||
<Card>
|
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
|
||||||
<CardTitle>
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
FAQ Data
|
Pelaporan Akun
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<div className="flex-none"></div>
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
|
||||||
New FAQ
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="grid md:grid-cols-3 gap-4">
|
||||||
|
<StatisticsBlock
|
||||||
|
total="64"
|
||||||
|
title="Total Pelaporan"
|
||||||
|
className=" border shadow-none"
|
||||||
|
/>
|
||||||
|
<StatisticsBlock
|
||||||
|
title="Total Disetujui"
|
||||||
|
total="564"
|
||||||
|
className=" border shadow-none"
|
||||||
|
chartColor="#FB8F65"
|
||||||
|
/>
|
||||||
|
<StatisticsBlock
|
||||||
|
title="Pelaporan Pending"
|
||||||
|
total="+5.0%"
|
||||||
|
className=" border shadow-none"
|
||||||
|
chartColor="#2563eb"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<FaqTable />
|
|
||||||
</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(() => {
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
@ -478,8 +505,10 @@ const DetailInfo = () => {
|
||||||
<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">
|
||||||
<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="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}
|
{detailDataImage?.category?.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center flex-wrap gap-2 mb-4">
|
<div className="flex justify-center flex-wrap gap-2 mb-4">
|
||||||
{detailDataImage?.tags?.split(",").map((tag: string) => (
|
{detailDataImage?.tags?.split(",").map((tag: string) => (
|
||||||
<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">
|
<a
|
||||||
|
onClick={() => router.push(`/all/filter?tag=${tag}`)}
|
||||||
|
key={tag}
|
||||||
|
className="bg-gray-200 text-gray-700 text-xs px-3 py-3 font-semibold 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,6 +469,16 @@ 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:hidden flex justify-end mb-2">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsFilterOpen(!isFilterOpen)}
|
||||||
|
className="text-sm text-white bg-[#bb3523] px-4 py-1 rounded-md shadow"
|
||||||
|
>
|
||||||
|
{isFilterOpen ? "Hide Filter" : "Show Filter"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isFilterOpen && (
|
||||||
<div className="lg:w-[25%] h-fit w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
|
<div className="lg:w-[25%] h-fit w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
|
||||||
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
|
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
|
||||||
<Icon icon="stash:filter-light" fontSize={30} />
|
<Icon icon="stash:filter-light" fontSize={30} />
|
||||||
|
|
@ -406,7 +487,10 @@ const FilterPage = () => {
|
||||||
<div className="border-t border-black my-4 dark:border-white"></div>
|
<div className="border-t border-black my-4 dark:border-white"></div>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
|
<label
|
||||||
|
htmlFor="search"
|
||||||
|
className="block text-sm font-medium text-gray-700 dark:text-white"
|
||||||
|
>
|
||||||
{t("search")}
|
{t("search")}
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -422,7 +506,9 @@ const FilterPage = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
|
<label className="block text-sm font-medium text-gray-700 dark:text-white">
|
||||||
|
{t("monthYear")}
|
||||||
|
</label>
|
||||||
<ReactDatePicker
|
<ReactDatePicker
|
||||||
selected={monthYearFilter}
|
selected={monthYearFilter}
|
||||||
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
|
||||||
|
|
@ -434,7 +520,9 @@ const FilterPage = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
|
<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">
|
<div className="flex flex-row justify justify-between gap-2">
|
||||||
<ReactDatePicker
|
<ReactDatePicker
|
||||||
selectsRange
|
selectsRange
|
||||||
|
|
@ -447,18 +535,44 @@ const FilterPage = () => {
|
||||||
placeholderText={t("selectDate")}
|
placeholderText={t("selectDate")}
|
||||||
onCalendarClose={() => setCalenderState(!calenderState)}
|
onCalendarClose={() => setCalenderState(!calenderState)}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
|
<div className="flex items-center">
|
||||||
|
{handleClose ? (
|
||||||
|
<Icon
|
||||||
|
icon="carbon:close-filled"
|
||||||
|
onClick={handleDeleteDate}
|
||||||
|
width="20"
|
||||||
|
inline
|
||||||
|
color="#216ba5"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
|
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||||
|
{t("categories")}
|
||||||
|
</h3>
|
||||||
<ul className="mt-2 space-y-2">
|
<ul className="mt-2 space-y-2">
|
||||||
{categories.map((category: any) => (
|
{categories.map((category: any) => (
|
||||||
<li key={category?.id}>
|
<li key={category?.id}>
|
||||||
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
|
<label
|
||||||
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
|
className="inline-flex items-center"
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
|
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>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
@ -468,36 +582,69 @@ const FilterPage = () => {
|
||||||
<div className="border-t border-black my-4 dark:border-white"></div>
|
<div className="border-t border-black my-4 dark:border-white"></div>
|
||||||
{/* Garis */}
|
{/* Garis */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
|
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||||
|
Format
|
||||||
|
</h3>
|
||||||
<ul className="mt-2 space-y-2">
|
<ul className="mt-2 space-y-2">
|
||||||
<li>
|
<li>
|
||||||
<label className="inline-flex items-center">
|
<label className="inline-flex items-center">
|
||||||
<Checkbox id="png" value="png" checked={formatFilter.includes("png")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "png")} />
|
<Checkbox
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">PNG</span>
|
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>
|
<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="jpeg"
|
||||||
|
value="jpeg"
|
||||||
|
checked={formatFilter.includes("jpeg")}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleFormatFilter(Boolean(e), "jpeg")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="ml-2 text-gray-700 dark:text-white">
|
||||||
|
JPEG
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label className="inline-flex items-center">
|
<label className="inline-flex items-center">
|
||||||
<Checkbox id="jpg" value="jpg" checked={formatFilter.includes("jpg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpg")} />
|
<Checkbox
|
||||||
<span className="ml-2 text-gray-700 dark:text-white">JPG</span>
|
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>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-black dark:border-white my-4"></div>
|
<div className="border-t border-black dark:border-white my-4"></div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
|
<a
|
||||||
|
onClick={cleanCheckbox}
|
||||||
|
className="text-[#bb3523] cursor-pointer"
|
||||||
|
>
|
||||||
<b>Reset Filter</b>
|
<b>Reset Filter</b>
|
||||||
</a>
|
</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";
|
||||||
|
|
@ -54,8 +54,10 @@ const Indeks: 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="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,12 +534,23 @@ 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">
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant={"outline"} className={cn("w-[240px] py-4 justify-start text-left font-normal", !startDate && "text-muted-foreground")}>
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
className={cn(
|
||||||
|
"w-[240px] py-3 justify-start text-left font-normal",
|
||||||
|
!startDate && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon />
|
||||||
{startDate ? format(startDate, "MMM yyyy") : <span>Pick a date</span>}
|
{startDate ? (
|
||||||
|
format(startDate, "MMM yyyy")
|
||||||
|
) : (
|
||||||
|
<span>Pick a date</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
|
|
@ -500,20 +565,41 @@ const Schedule = (props: any) => {
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<div className="container relative py-4">
|
<div className="container relative py-4 flex flex-row items-center gap-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">
|
<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 ">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
<svg
|
||||||
<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" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
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>
|
</svg>
|
||||||
Filter
|
Filter
|
||||||
<svg className="flex items-center justify-center" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
<svg
|
||||||
<path fill="currentColor" fill-rule="evenodd" d="m6 7l6 6l6-6l2 2l-8 8l-8-8z" />
|
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>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent align="start" className="flex p-0 rounded-md w-fit">
|
<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="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">
|
<div className="gap-6 flex flex-row justify-end">
|
||||||
<p className="font-semibold">Filter</p>
|
<p className="font-semibold">Filter</p>
|
||||||
|
|
@ -527,7 +613,14 @@ const Schedule = (props: any) => {
|
||||||
{city?.map((list) => (
|
{city?.map((list) => (
|
||||||
<div className="mt-2 gap-2 flex flex-row">
|
<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} />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className=""
|
||||||
|
id={`filterCategory-${list.key}`}
|
||||||
|
value={list.id}
|
||||||
|
checked={regionFilter?.includes(list.id)}
|
||||||
|
onChange={handleRegionFilter}
|
||||||
|
/>
|
||||||
<p>{list?.name}</p>
|
<p>{list?.name}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -538,15 +631,24 @@ const Schedule = (props: any) => {
|
||||||
<div className="flex flex-col lg:flex-row gap-3">
|
<div className="flex flex-col lg:flex-row gap-3">
|
||||||
{regionName?.map((list: any) => (
|
{regionName?.map((list: any) => (
|
||||||
<div className="text-left">
|
<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">
|
<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}
|
{list.name}
|
||||||
<Icon icon="icon-park-outline:delete-two" className="items-center" />
|
<Icon
|
||||||
|
icon="icon-park-outline:delete-two"
|
||||||
|
className="items-center"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col lg:flex-row gap-6">
|
</div>
|
||||||
|
<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,8 +483,8 @@ 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
|
<Swiper
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
|
|
@ -525,12 +527,13 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="w-full lg:w-4/12">
|
<div className="w-full lg:w-4/12">
|
||||||
<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">
|
||||||
|
<Link href={"/contributor/content/image"}>
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button type="submit" color="primary" variant="outline">
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</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,7 +486,8 @@ export default function FormTeksDetail() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Label className="text-xl text-black">File Media</Label>
|
<div className="space-y-2">
|
||||||
|
<Label className="text-xl">{t("file-media")} </Label>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Swiper
|
<Swiper
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
|
|
@ -512,7 +513,9 @@ export default function FormTeksDetail() {
|
||||||
src={data.url}
|
src={data.url}
|
||||||
title={data.fileName || "PDF File"}
|
title={data.fileName || "PDF File"}
|
||||||
/>
|
/>
|
||||||
) : [".docx", ".ppt", ".pptx"].includes(data.format) ? (
|
) : [".docx", ".ppt", ".pptx"].includes(
|
||||||
|
data.format
|
||||||
|
) ? (
|
||||||
// Menampilkan file dokumen menggunakan Office Viewer
|
// Menampilkan file dokumen menggunakan Office Viewer
|
||||||
<iframe
|
<iframe
|
||||||
className="w-full h-96 rounded-md"
|
className="w-full h-96 rounded-md"
|
||||||
|
|
@ -565,12 +568,13 @@ export default function FormTeksDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="w-full lg:w-4/12">
|
<div className="w-full lg:w-4/12">
|
||||||
<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,8 +477,8 @@ 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
|
<Swiper
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
|
|
@ -523,12 +523,13 @@ export default function FormVideoDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="w-full lg:w-4/12">
|
<div className="w-full lg:w-4/12">
|
||||||
<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,20 +580,49 @@ 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">
|
||||||
|
<Tabs defaultValue="national" className="w-[350px]">
|
||||||
|
<TabsList className="grid w-full grid-cols-3 border">
|
||||||
|
<TabsTrigger value="national">Nasional</TabsTrigger>
|
||||||
|
<TabsTrigger value="polda">Polda</TabsTrigger>
|
||||||
|
<TabsTrigger value="satker">Satker</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="national">
|
||||||
{heroData?.map((item: any) => (
|
{heroData?.map((item: any) => (
|
||||||
<li key={item?.id} className="flex gap-4 flex-row lg:w-full ">
|
<li
|
||||||
|
key={item?.id}
|
||||||
|
className="flex gap-4 flex-row lg:w-full mx-2"
|
||||||
|
>
|
||||||
<div className="flex-shrink-0 w-24 rounded-lg">
|
<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" />
|
<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>
|
||||||
<div className="w-[280px] lg:w-auto">
|
<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>
|
<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}`}>
|
<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>
|
<h3 className="text-base font-bold mt-2 h-6 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
|
||||||
|
{item?.title}
|
||||||
|
</h3>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-[10px] flex flex-row items-center gap-1 text-gray-500 mt-1">
|
<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"} |{" "}
|
{formatDateToIndonesian(new Date(item?.createdAt))}{" "}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
|
{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
|
<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"
|
||||||
|
|
@ -185,6 +633,104 @@ const Hero: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</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>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue