pull main
This commit is contained in:
commit
fbc0db0ab4
|
|
@ -87,7 +87,7 @@ export default function ContentManagement() {
|
|||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-intl?"
|
||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-intl?"
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-intl?provinsi-polda=${poldaState}&`;
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default function EmergencyIssue() {
|
|||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?"
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${provState}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default function FeedbackCenter() {
|
|||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-tickets?"
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-tickets?provinsi-polda=${provState}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export default function ContentManagement() {
|
|||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act-jnl?"
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act-jnl?provinsi-polda=${poldaState}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ import { Button } from "@/components/ui/button";
|
|||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { format } from "date-fns";
|
||||
import header from "@/components/partials/header";
|
||||
import { date } from "zod";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -23,50 +26,61 @@ const columns: ColumnDef<any>[] = [
|
|||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Tanggal",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("createdAt")}</span>
|
||||
),
|
||||
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: "account-type",
|
||||
accessorKey: "createdByCategory",
|
||||
header: "Jenis Akun",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("account-type")}</span>
|
||||
<span className="normal-case">{row.getValue("createdByCategory")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "userName",
|
||||
accessorKey: "createdByUsername",
|
||||
header: "UserName",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("userName")}</span>
|
||||
<span className="normal-case">{row.getValue("createdByUsername")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "accessMediahub",
|
||||
accessorKey: "accessFrequency",
|
||||
header: "Akses Mediahub",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("accessMediahub")}</span>
|
||||
<span className="normal-case">{row.getValue("accessFrequency")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "desaignWeb",
|
||||
accessorKey: "uiExperienceDesign",
|
||||
header: "Tampilan Desain Web",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("desaignWeb")}</span>
|
||||
<span className="normal-case">{row.getValue("uiExperienceDesign")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "navigation",
|
||||
accessorKey: "uiExperienceNavigation",
|
||||
header: "Kemudahan Navigasi",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("navigation")}</span>
|
||||
<span className="normal-case">
|
||||
{row.getValue("uiExperienceNavigation")}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "fastAccess",
|
||||
accessorKey: "uiExperienceSpeed",
|
||||
header: "Kecepatan Akses",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("fastAccess")}</span>
|
||||
<span className="normal-case">{row.getValue("uiExperienceSpeed")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -86,13 +100,12 @@ const columns: ColumnDef<any>[] = [
|
|||
</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>
|
||||
<Link href={`/admin/survey/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 mt-1" />
|
||||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ import {
|
|||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import { getSurveyData } from "@/service/survey/survey";
|
||||
|
||||
const data = [
|
||||
{
|
||||
|
|
@ -181,7 +182,7 @@ const SurveyListTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const res = await getMediaBlastCampaignPage(page - 1);
|
||||
const res = await getSurveyData();
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
|
|
@ -249,7 +250,7 @@ const SurveyListTable = () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-h-screen p-3">
|
||||
<div className="p-3">
|
||||
<h2 className="text-center font-semibold mb-4">
|
||||
Survei Kepuasan Pengguna MediaHub Polri
|
||||
</h2>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormBlogDetail from "@/components/form/blog/blog--detail-form";
|
||||
import FormSurvey from "@/components/landing-page/survey";
|
||||
import FormSurveyDetail from "@/components/form/survey/survey-detail";
|
||||
|
||||
const SurveyDetailPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormSurveyDetail />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SurveyDetailPage;
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormBlogDetail from "@/components/form/blog/blog--detail-form";
|
||||
import FormSurveyDetailPage from "@/components/form/survey/survey-detail";
|
||||
|
||||
const BlogDetailPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormBlogDetail />
|
||||
<FormSurveyDetailPage />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|||
import columns from "./columns";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
|
@ -192,56 +193,86 @@ const TableSPIT = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex items-center py-4">
|
||||
<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>
|
||||
<div className="mx-2 my-1">
|
||||
<Label>{t("date")}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={dateFilter}
|
||||
onChange={(e) => setDateFilter(e.target.value)}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
<Label className="ml-2 mt-2">Status</Label>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="status-2"
|
||||
className="mr-2"
|
||||
checked={statusFilter.includes(1)}
|
||||
onChange={() => handleStatusCheckboxChange(1)}
|
||||
/>
|
||||
<label htmlFor="status-2" 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 className="flex fle-row items-center gap-3">
|
||||
<div className="flex items-center py-4">
|
||||
<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>
|
||||
<div className="mx-2 my-1">
|
||||
<Label>{t("date")}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={dateFilter}
|
||||
onChange={(e) => setDateFilter(e.target.value)}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
<Label className="ml-2 mt-2">Status</Label>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="status-2"
|
||||
className="mr-2"
|
||||
checked={statusFilter.includes(1)}
|
||||
onChange={() => handleStatusCheckboxChange(1)}
|
||||
/>
|
||||
<label htmlFor="status-2" 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 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>
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
|
|
@ -39,6 +40,7 @@ import {
|
|||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
|
|
@ -163,17 +165,47 @@ const MediahubTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
||||
<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 className="flex flex-row items-center gap-3">
|
||||
<div className="w-[150px] md:w-[250px] lg:w-[250px] border border-black rounded-md">
|
||||
<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 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">
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
|
|
@ -39,6 +40,7 @@ import {
|
|||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
|
|
@ -162,17 +164,47 @@ const MedsosTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
||||
<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 className="flex flex-row items-center gap-3">
|
||||
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
||||
<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 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">
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const useTableColumns = ({
|
|||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: t("upload-date"),
|
||||
header: t("generate-date"),
|
||||
cell: ({ row }) => {
|
||||
const createdAt = row.getValue("createdAt") as
|
||||
| string
|
||||
|
|
@ -69,6 +69,12 @@ const useTableColumns = ({
|
|||
cell: ({ row }) => <span className="">{row.getValue("version")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: t("status"),
|
||||
cell: ({ row }) => <span className="">{row.getValue("status")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import { Button } from "@/components/ui/button";
|
|||
import useTableColumns from "./columns";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
|
|
@ -172,8 +173,8 @@ const EventTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<div className="mx-3">
|
||||
<div className="flex flex-row gap-3 items-center">
|
||||
<div className="">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
|
|
@ -244,6 +245,34 @@ const EventTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import { useTranslations } from "next-intl";
|
|||
import useTableColumns from "./columns";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
|
|
@ -186,8 +187,8 @@ const PressConferenceTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3">
|
||||
<div className="mx-3">
|
||||
<div className="flex flex-row items-center gap-3">
|
||||
<div className="">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
|
|
@ -258,6 +259,34 @@ const PressConferenceTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import { Link } from "@/i18n/routing";
|
|||
import useTableColumns from "./columns";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
|
|
@ -187,8 +188,8 @@ const PressReleaseTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<div className="mx-3">
|
||||
<div className="flex flex-row items-center gap-3">
|
||||
<div className="">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
|
|
@ -259,6 +260,34 @@ const PressReleaseTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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 { Eye, MoreVertical, SquarePen, Trash2, Upload } from "lucide-react";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -105,6 +105,7 @@ const useTableColumns = () => {
|
|||
cell: ({ row }) => {
|
||||
const router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||
|
||||
async function deleteProcess(id: any) {
|
||||
loading();
|
||||
|
|
@ -170,6 +171,16 @@ const useTableColumns = () => {
|
|||
Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
{roleId == 12 && (
|
||||
<Link
|
||||
href={`/contributor/task-ta/upload-task/${row.original.id}`}
|
||||
>
|
||||
<DropdownMenuItem 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" />
|
||||
Upload Tugas
|
||||
</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"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ 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 {
|
||||
checkAuthorization,
|
||||
checkLoginSession,
|
||||
getCookiesDecrypt,
|
||||
} from "@/lib/utils";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import TaskTaTable from "./components/task-ta-table";
|
||||
|
|
@ -20,6 +24,7 @@ const TaskTaPage = () => {
|
|||
|
||||
initState();
|
||||
}, []);
|
||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -33,12 +38,14 @@ const TaskTaPage = () => {
|
|||
{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>
|
||||
{roleId !== 12 && (
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTaskTa from "@/components/form/task-ta/task-ta-form";
|
||||
import FormTaskTaNew from "@/components/form/task-ta/task-ta-upload-form";
|
||||
|
||||
const TaskTaUploadPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormTaskTaNew />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaskTaUploadPage;
|
||||
|
|
@ -40,6 +40,7 @@ import {
|
|||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
|
|
@ -338,6 +339,35 @@ const TaskTable = () => {
|
|||
</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 className="flex-none">
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ import {
|
|||
import { getOnlyDate } from "@/utils/globals";
|
||||
import { useParams } from "next/navigation";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
|
|
@ -118,7 +119,19 @@ const units = [
|
|||
id: "3",
|
||||
label: "Polres",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Satker",
|
||||
},
|
||||
];
|
||||
|
||||
const ViewEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function DetailDaily() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -725,16 +738,10 @@ export default function DetailDaily() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
config={{ readonly: true }}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<ViewEditor initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ import {
|
|||
import { getOnlyDate } from "@/utils/globals";
|
||||
import { useParams } from "next/navigation";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
|
|
@ -118,7 +119,18 @@ const units = [
|
|||
id: "3",
|
||||
label: "Polres",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Satker",
|
||||
},
|
||||
];
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function EditDaily() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -728,15 +740,10 @@ export default function EditDaily() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import {
|
|||
savePlanning,
|
||||
} from "@/service/agenda-setting/agenda-setting";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
|
|
@ -116,7 +117,18 @@ const units = [
|
|||
id: "3",
|
||||
label: "Polres",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Satker",
|
||||
},
|
||||
];
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function CreateDaily() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const [listDest, setListDest] = useState<any>([]);
|
||||
|
|
@ -694,15 +706,10 @@ export default function CreateDaily() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { close, error, loading } from "@/config/swal";
|
|||
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
month: z.date({
|
||||
|
|
@ -44,6 +45,13 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const ViewEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function DetailMonthly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -215,16 +223,10 @@ export default function DetailMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
config={{ readonly: true }}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<ViewEditor initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { close, error, loading } from "@/config/swal";
|
|||
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
month: z.date({
|
||||
|
|
@ -44,6 +45,14 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function EditMonthly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -108,6 +117,8 @@ export default function EditMonthly() {
|
|||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const month = new Date(data.month).getMonth() + 1;
|
||||
const year = new Date(data.month).getFullYear();
|
||||
const reqData = {
|
||||
id: id,
|
||||
planningTypeId: 1,
|
||||
|
|
@ -115,9 +126,7 @@ export default function EditMonthly() {
|
|||
time: "3",
|
||||
description: data.detail,
|
||||
username: "",
|
||||
date: `${new Date(data.month).getMonth() + 1}/${new Date(
|
||||
data.month
|
||||
).getFullYear()}`,
|
||||
date: `${month.toString().padStart(2, "0")}/${year}`,
|
||||
status: "Open",
|
||||
};
|
||||
console.log("req", reqData, data.month);
|
||||
|
|
@ -214,15 +223,10 @@ export default function EditMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ import Swal from "sweetalert2";
|
|||
import withReactContent from "sweetalert2-react-content";
|
||||
import { error } from "@/config/swal";
|
||||
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
|
||||
import month from "react-datepicker/dist/month";
|
||||
import year from "react-datepicker/dist/year";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
month: z.date({
|
||||
|
|
@ -42,6 +45,12 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function CreateMonthly() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -77,15 +86,15 @@ export default function CreateMonthly() {
|
|||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const month = new Date(data.month).getMonth() + 1;
|
||||
const year = new Date(data.month).getFullYear();
|
||||
const reqData = {
|
||||
planningTypeId: 1,
|
||||
title: data.title,
|
||||
time: "3",
|
||||
description: data.detail,
|
||||
username: "",
|
||||
date: `${new Date(data.month).getMonth() + 1}/${new Date(
|
||||
data.month
|
||||
).getFullYear()}`,
|
||||
// username: "",
|
||||
date: `${month.toString().padStart(2, "0")}/${year}`,
|
||||
status: "Open",
|
||||
};
|
||||
console.log("req", reqData, data.month);
|
||||
|
|
@ -110,6 +119,7 @@ export default function CreateMonthly() {
|
|||
|
||||
const handleMonthSelect = (selectedDate: Date | undefined) => {
|
||||
if (!selectedDate) return;
|
||||
// Set ke tanggal 1 agar tidak ada hari yang diambil
|
||||
const newDate = new Date(
|
||||
selectedDate.getFullYear(),
|
||||
selectedDate.getMonth(),
|
||||
|
|
@ -198,15 +208,10 @@ export default function CreateMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import {
|
|||
import dayjs from "dayjs";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
week: z.object({
|
||||
|
|
@ -61,7 +62,15 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
export default function DetailMonthly() {
|
||||
|
||||
const ViewEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function DetailWeekly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -227,16 +236,10 @@ export default function DetailMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
config={{ readonly: true }}
|
||||
/>
|
||||
<ViewEditor initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import {
|
|||
import dayjs from "dayjs";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
week: z.object({
|
||||
|
|
@ -61,7 +62,14 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
export default function EditMonthly() {
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function EditWeekly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -225,15 +233,10 @@ export default function EditMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,14 @@ import {
|
|||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import dayjs from "dayjs";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
const FormSchema = z.object({
|
||||
week: z.object({
|
||||
|
|
@ -260,15 +268,10 @@ export default function CreateMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -65,6 +65,16 @@ const columns: ColumnDef<any>[] = [
|
|||
Detail
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`/curator/task-plan/medsos-mediahub/create-daily/edit/${row.original.id}`}
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<a className="text-red-600">Delete</a>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,766 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import JoditEditor from "jodit-react";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { getUserLevelForAssignments } from "@/service/task";
|
||||
import { list } from "postcss";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import { id } from "date-fns/locale";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
getWeeklyPlanList,
|
||||
savePlanning,
|
||||
} from "@/service/agenda-setting/agenda-setting";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import { useParams } from "next/navigation";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
required_error: "Required",
|
||||
}),
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
detail: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
output: z.array(z.string()).refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
unit: z.array(z.string()).refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
type: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
parentId: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: "2",
|
||||
label: "Audio Visual",
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
label: "Foto",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Audio",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Text",
|
||||
},
|
||||
];
|
||||
|
||||
const units = [
|
||||
{
|
||||
id: "1",
|
||||
label: "Mabes Polri",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Polda",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Polres",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Satker",
|
||||
},
|
||||
];
|
||||
|
||||
const ViewEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function DetailDaily() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const [listDest, setListDest] = useState<any>([]);
|
||||
const router = useRouter();
|
||||
const [weeklyList, setWeeklyList] = useState<any>();
|
||||
const [selected, setSelected] = useState<{ [key: string]: boolean }>({});
|
||||
const [selectAll, setSelectAll] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, [id]);
|
||||
|
||||
async function initFetch() {
|
||||
if (id != undefined) {
|
||||
loading();
|
||||
const res = await getPlanningById(id);
|
||||
close();
|
||||
|
||||
if (res?.data?.data != undefined) {
|
||||
const data = res?.data?.data;
|
||||
console.log("data");
|
||||
console.log("Data :", data);
|
||||
form.setValue("title", data.title);
|
||||
form.setValue("detail", data.description);
|
||||
form.setValue("date", new Date(data.date));
|
||||
form.setValue(
|
||||
"output",
|
||||
data.fileTypeOutput.split(",")?.length > 1
|
||||
? data.fileTypeOutput.split(",")
|
||||
: [data.fileTypeOutput]
|
||||
);
|
||||
form.setValue(
|
||||
"unit",
|
||||
data.assignedToLevel.split(",")?.length > 1
|
||||
? data.assignedToLevel.split(",")
|
||||
: [data.assignedToLevel]
|
||||
);
|
||||
form.setValue("type", String(data?.assignmentTypeId));
|
||||
form.setValue("parentId", String(data?.parentId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getWeeklyPlanning();
|
||||
}, []);
|
||||
|
||||
async function getWeeklyPlanning() {
|
||||
const res = await getWeeklyPlanList(new Date().getDate(), 2);
|
||||
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
value: String(option.id),
|
||||
}));
|
||||
setWeeklyList(optionArr);
|
||||
}
|
||||
}
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
unit: [],
|
||||
output: [],
|
||||
detail: "",
|
||||
},
|
||||
});
|
||||
const editor = useRef(null);
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
console.log("data", data);
|
||||
if (form.getValues("detail") == "") {
|
||||
form.setError("detail", {
|
||||
type: "manual",
|
||||
message: "Required",
|
||||
});
|
||||
} else {
|
||||
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>) => {
|
||||
const getSelectedString = () => {
|
||||
return Object.keys(selected)
|
||||
.filter((key) => selected[key])
|
||||
.join(", ");
|
||||
};
|
||||
console.log("data", data, selected);
|
||||
loading();
|
||||
|
||||
const reqData = {
|
||||
planningTypeId: 2,
|
||||
time: "1",
|
||||
title: data.title,
|
||||
assignmentTypeId: data.type, //string
|
||||
description: data.detail,
|
||||
assignedToLevel: unit?.join(","), //string
|
||||
assignmentPurpose: getSelectedString(), //string
|
||||
fileTypeOutput: data.output?.join(","), //string
|
||||
status: "Open",
|
||||
date: getOnlyDate(data.date),
|
||||
// date:
|
||||
// isPublish || isUpdate
|
||||
// ? selectedDate?.length > 10
|
||||
// ? data.date?.toISOString().slice(0, 10)
|
||||
// : selectedDate
|
||||
// : data.date?.toISOString().slice(0, 10),
|
||||
parentId: Number(data.parentId), //number
|
||||
assignmentMainTypeId: 1,
|
||||
};
|
||||
|
||||
console.log("req =>", reqData);
|
||||
const response = await savePlanning(reqData);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/curator/task-plan/mediahub");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const output = form.watch("output");
|
||||
|
||||
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||
|
||||
const unit = form.watch("unit");
|
||||
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"output",
|
||||
items.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("output", []);
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"output",
|
||||
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||
);
|
||||
};
|
||||
|
||||
const handleAllUnitCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"unit",
|
||||
units.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("unit", []);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
}
|
||||
form.setValue("unit", temp);
|
||||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await getUserLevelForAssignments();
|
||||
setListDest(response?.data?.data.list);
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const handleParentChange = (listId: string) => {
|
||||
setSelected((prev) => ({
|
||||
...prev,
|
||||
[listId]: !prev[listId],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSelectAllPolres = (listId: string, isChecked: boolean) => {
|
||||
setSelectAll((prev) => ({
|
||||
...prev,
|
||||
[listId]: isChecked,
|
||||
}));
|
||||
|
||||
setSelected((prev) => {
|
||||
const updatedState = { ...prev };
|
||||
listDest
|
||||
.find((list: any) => list.id === listId)
|
||||
?.subDestination.forEach((subDes: any) => {
|
||||
updatedState[`${listId}${subDes.id}`] = isChecked;
|
||||
});
|
||||
return updatedState;
|
||||
});
|
||||
};
|
||||
|
||||
const handleChildChange = (childId: string) => {
|
||||
setSelected((prev) => ({
|
||||
...prev,
|
||||
[childId]: !prev[childId],
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col bg-white gap-2 p-6">
|
||||
<p className="text-lg">Perencanaan MediaHub</p>
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Judul Perencanaan</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul Perencanaan"
|
||||
onChange={field.onChange}
|
||||
readOnly
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="output"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<div className="mb-4">
|
||||
<FormLabel className="text-sm">Output Tugas</FormLabel>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 items-center ">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllChecked}
|
||||
onCheckedChange={(checked) =>
|
||||
handleAllCheckedChange(checked)
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{items.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="output"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={output?.includes(item.id)}
|
||||
onCheckedChange={(checked) =>
|
||||
handleItemCheckedChange(item.id, checked)
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.label}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="unit"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<div className="mb-4">
|
||||
<FormLabel className="text-sm">Pelaksana Tugas</FormLabel>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 items-center ">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllUnitChecked}
|
||||
onCheckedChange={(checked) =>
|
||||
handleAllUnitCheckedChange(checked)
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{units.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="unit"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={unit?.includes(item.id)}
|
||||
// disabled={
|
||||
// item.id === "3" && !unit.includes("2")
|
||||
// }
|
||||
onCheckedChange={(checked) =>
|
||||
handleUnitCheckedChange(item.id, checked)
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.label}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Dialog>
|
||||
<DialogTrigger disabled>
|
||||
<a className="text-primary cursor-pointer text-sm">
|
||||
{`[Kustom]`}
|
||||
</a>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Daftar Wilayah Polda dan Polres
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-4 py-4 px-2 max-h-[600px] overflow-y-auto custom-scrollbar-table">
|
||||
{listDest?.map((list: any) => (
|
||||
<div key={list.id}>
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="h-fit border-none"
|
||||
>
|
||||
<AccordionItem
|
||||
value={list.name}
|
||||
className="border-none"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{list.name}
|
||||
</label>
|
||||
<AccordionTrigger className="w-fit bg-transparent"></AccordionTrigger>
|
||||
</div>
|
||||
<AccordionContent>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
onCheckedChange={(e) =>
|
||||
handleSelectAllPolres(
|
||||
list.id,
|
||||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Pilih Semua Polres
|
||||
</label>
|
||||
</div>
|
||||
{list.subDestination.map(
|
||||
(subDes: any) => (
|
||||
<div
|
||||
key={subDes.id}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleChildChange(
|
||||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{subDes.name}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="type"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Jenis Penugasan</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="flex flex-row gap-3"
|
||||
disabled
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="1" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Publikasi
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="2" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Amplifikasi
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="3" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Kontra</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="date"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col">
|
||||
<FormLabel>Pilih Tanggal</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] justify-start text-left font-normal",
|
||||
!field.value && "text-muted-foreground"
|
||||
)}
|
||||
disabled
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{field.value ? (
|
||||
format(field.value, "PPP")
|
||||
) : (
|
||||
<span>Masukkan Tanggal</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={field.value}
|
||||
onSelect={field.onChange}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parentId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Perencanaan Mingguan</FormLabel>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
disabled
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{weeklyList?.map((list: any) => (
|
||||
<SelectItem key={list.id} value={list.value}>
|
||||
{list.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<ViewEditor initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
|
||||
<Button
|
||||
onClick={() => router.back()}
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,771 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import JoditEditor from "jodit-react";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { getUserLevelForAssignments } from "@/service/task";
|
||||
import { list } from "postcss";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import { id, te } from "date-fns/locale";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
getWeeklyPlanList,
|
||||
savePlanning,
|
||||
} from "@/service/agenda-setting/agenda-setting";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import { useParams } from "next/navigation";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
required_error: "Required",
|
||||
}),
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
detail: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
output: z.array(z.string()).refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
unit: z.array(z.string()).refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
type: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
parentId: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: "2",
|
||||
label: "Audio Visual",
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
label: "Foto",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Audio",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Text",
|
||||
},
|
||||
];
|
||||
|
||||
const units = [
|
||||
{
|
||||
id: "1",
|
||||
label: "Mabes Polri",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Polda",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Polres",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Satker",
|
||||
},
|
||||
];
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function EditDaily() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const [listDest, setListDest] = useState<any>([]);
|
||||
const router = useRouter();
|
||||
const [weeklyList, setWeeklyList] = useState<any>();
|
||||
const [selected, setSelected] = useState<{ [key: string]: boolean }>({});
|
||||
const [selectAll, setSelectAll] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, [id]);
|
||||
|
||||
async function initFetch() {
|
||||
if (id != undefined) {
|
||||
loading();
|
||||
const res = await getPlanningById(id);
|
||||
close();
|
||||
|
||||
if (res?.data?.data != undefined) {
|
||||
const data = res?.data?.data;
|
||||
console.log("data");
|
||||
console.log("Data :", data);
|
||||
form.setValue("title", data.title);
|
||||
form.setValue("detail", data.description);
|
||||
form.setValue("date", new Date(data.date));
|
||||
form.setValue(
|
||||
"output",
|
||||
data.fileTypeOutput.split(",")?.length > 1
|
||||
? data.fileTypeOutput.split(",")
|
||||
: [data.fileTypeOutput]
|
||||
);
|
||||
form.setValue("type", String(data?.assignmentTypeId));
|
||||
form.setValue("parentId", String(data?.parentId));
|
||||
mapTopDestination(data?.assignedToLevel);
|
||||
mapDestination(data?.assignedToTopLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapTopDestination = (data: string) => {
|
||||
const temp: string[] = [];
|
||||
data.split(",").map((list) => {
|
||||
if (list.length < 2) {
|
||||
temp.push(list);
|
||||
}
|
||||
});
|
||||
form.setValue("unit", temp);
|
||||
};
|
||||
|
||||
const mapDestination = (data: string) => {
|
||||
const temp: { [key: number]: boolean } = {};
|
||||
data.split(",").forEach((list) => {
|
||||
temp[Number(list)] = true;
|
||||
});
|
||||
setSelected(temp);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getWeeklyPlanning();
|
||||
}, []);
|
||||
|
||||
async function getWeeklyPlanning() {
|
||||
const res = await getWeeklyPlanList(new Date().getDate(), 2);
|
||||
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
value: String(option.id),
|
||||
}));
|
||||
setWeeklyList(optionArr);
|
||||
}
|
||||
}
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
unit: [],
|
||||
output: [],
|
||||
detail: "",
|
||||
},
|
||||
});
|
||||
const editor = useRef(null);
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
console.log("data", data);
|
||||
if (form.getValues("detail") == "") {
|
||||
form.setError("detail", {
|
||||
type: "manual",
|
||||
message: "Required",
|
||||
});
|
||||
} else {
|
||||
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>) => {
|
||||
const getSelectedString = () => {
|
||||
return Object.keys(selected)
|
||||
.filter((key) => selected[key])
|
||||
.join(", ");
|
||||
};
|
||||
console.log("data", data, selected);
|
||||
loading();
|
||||
|
||||
const reqData = {
|
||||
id: id,
|
||||
planningTypeId: 2,
|
||||
time: "1",
|
||||
title: data.title,
|
||||
assignmentTypeId: data.type, //string
|
||||
description: data.detail,
|
||||
assignedToLevel: unit?.join(","), //string
|
||||
assignmentPurpose: getSelectedString(), //string
|
||||
fileTypeOutput: data.output?.join(","), //string
|
||||
status: "Open",
|
||||
date: getOnlyDate(data.date),
|
||||
// date:
|
||||
// isPublish || isUpdate
|
||||
// ? selectedDate?.length > 10
|
||||
// ? data.date?.toISOString().slice(0, 10)
|
||||
// : selectedDate
|
||||
// : data.date?.toISOString().slice(0, 10),
|
||||
parentId: Number(data.parentId), //number
|
||||
assignmentMainTypeId: 1,
|
||||
};
|
||||
|
||||
const response = await savePlanning(reqData);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/curator/task-plan/mediahub");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const output = form.watch("output");
|
||||
|
||||
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||
|
||||
const unit = form.watch("unit");
|
||||
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"output",
|
||||
items.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("output", []);
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"output",
|
||||
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||
);
|
||||
};
|
||||
|
||||
const handleAllUnitCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"unit",
|
||||
units.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("unit", []);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
}
|
||||
form.setValue("unit", temp);
|
||||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await getUserLevelForAssignments();
|
||||
setListDest(response?.data?.data.list);
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const handleParentChange = (listId: string) => {
|
||||
setSelected((prev) => ({
|
||||
...prev,
|
||||
[listId]: !prev[listId],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSelectAllPolres = (listId: string, isChecked: boolean) => {
|
||||
setSelectAll((prev) => ({
|
||||
...prev,
|
||||
[listId]: isChecked,
|
||||
}));
|
||||
|
||||
setSelected((prev) => {
|
||||
const updatedState = { ...prev };
|
||||
listDest
|
||||
.find((list: any) => list.id === listId)
|
||||
?.subDestination.forEach((subDes: any) => {
|
||||
updatedState[`${listId}${subDes.id}`] = isChecked;
|
||||
});
|
||||
return updatedState;
|
||||
});
|
||||
};
|
||||
|
||||
const handleChildChange = (childId: string) => {
|
||||
setSelected((prev) => ({
|
||||
...prev,
|
||||
[childId]: !prev[childId],
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col bg-white gap-2 p-6">
|
||||
<p className="text-lg">Perencanaan MediaHub</p>
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Judul Perencanaan</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul Perencanaan"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="output"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<div className="mb-4">
|
||||
<FormLabel className="text-sm">Output Tugas</FormLabel>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 items-center ">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllChecked}
|
||||
onCheckedChange={(checked) =>
|
||||
handleAllCheckedChange(checked)
|
||||
}
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{items.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="output"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={output?.includes(item.id)}
|
||||
onCheckedChange={(checked) =>
|
||||
handleItemCheckedChange(item.id, checked)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.label}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="unit"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<div className="mb-4">
|
||||
<FormLabel className="text-sm">Pelaksana Tugas</FormLabel>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 items-center ">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllUnitChecked}
|
||||
onCheckedChange={(checked) =>
|
||||
handleAllUnitCheckedChange(checked)
|
||||
}
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{units.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="unit"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={unit?.includes(item.id)}
|
||||
disabled={
|
||||
item.id === "3" && !unit.includes("2")
|
||||
}
|
||||
onCheckedChange={(checked) =>
|
||||
handleUnitCheckedChange(item.id, checked)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.label}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<a className="text-primary cursor-pointer text-sm">
|
||||
{`[Kustom]`}
|
||||
</a>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Daftar Wilayah Polda dan Polres
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-4 py-4 px-2 max-h-[600px] overflow-y-auto custom-scrollbar-table">
|
||||
{listDest?.map((list: any) => (
|
||||
<div key={list.id}>
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="h-fit border-none"
|
||||
>
|
||||
<AccordionItem
|
||||
value={list.name}
|
||||
className="border-none"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{list.name}
|
||||
</label>
|
||||
<AccordionTrigger className="w-fit bg-transparent"></AccordionTrigger>
|
||||
</div>
|
||||
<AccordionContent>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
onCheckedChange={(e) =>
|
||||
handleSelectAllPolres(
|
||||
list.id,
|
||||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Pilih Semua Polres
|
||||
</label>
|
||||
</div>
|
||||
{list.subDestination.map(
|
||||
(subDes: any) => (
|
||||
<div
|
||||
key={subDes.id}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleChildChange(
|
||||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{subDes.name}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="type"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Jenis Penugasan</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="flex flex-row gap-3"
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="1" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Publikasi
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="2" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Amplifikasi
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="3" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Kontra</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="date"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col">
|
||||
<FormLabel>Pilih Tanggal</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] justify-start text-left font-normal",
|
||||
!field.value && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{field.value ? (
|
||||
format(field.value, "PPP")
|
||||
) : (
|
||||
<span>Masukkan Tanggal</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={field.value}
|
||||
onSelect={field.onChange}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parentId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Perencanaan Mingguan</FormLabel>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{weeklyList?.map((list: any) => (
|
||||
<SelectItem key={list.id} value={list.value}>
|
||||
{list.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
|
||||
<Button
|
||||
onClick={() => router.back()}
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button type="submit" color="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ import {
|
|||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { getUserLevelForAssignments } from "@/service/task";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
|
|
@ -141,8 +142,20 @@ const units = [
|
|||
id: "3",
|
||||
label: "Polres",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "Satker",
|
||||
},
|
||||
];
|
||||
export default function CreateMonthly() {
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function CreateDaily() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const [weeklyList, setWeeklyList] = useState<any>();
|
||||
const router = useRouter();
|
||||
|
|
@ -760,15 +773,10 @@ export default function CreateMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { close, error, loading } from "@/config/swal";
|
|||
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
month: z.date({
|
||||
|
|
@ -44,6 +45,13 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const ViewEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function DetailMonthly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -108,15 +116,15 @@ export default function DetailMonthly() {
|
|||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const month = new Date(data.month).getMonth() + 1;
|
||||
const year = new Date(data.month).getFullYear();
|
||||
const reqData = {
|
||||
planningTypeId: 1,
|
||||
title: data.title,
|
||||
time: "3",
|
||||
description: data.detail,
|
||||
username: "",
|
||||
date: `${new Date(data.month).getMonth() + 1}/${new Date(
|
||||
data.month
|
||||
).getFullYear()}`,
|
||||
date: `${month.toString().padStart(2, "0")}/${year}`,
|
||||
status: "Open",
|
||||
};
|
||||
console.log("req", reqData, data.month);
|
||||
|
|
@ -215,16 +223,10 @@ export default function DetailMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
config={{ readonly: true }}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<ViewEditor initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { close, error, loading } from "@/config/swal";
|
|||
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
month: z.date({
|
||||
|
|
@ -44,6 +45,13 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function EditMonthly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -108,6 +116,8 @@ export default function EditMonthly() {
|
|||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const month = new Date(data.month).getMonth() + 1;
|
||||
const year = new Date(data.month).getFullYear();
|
||||
const reqData = {
|
||||
id: id,
|
||||
planningTypeId: 1,
|
||||
|
|
@ -115,9 +125,7 @@ export default function EditMonthly() {
|
|||
time: "3",
|
||||
description: data.detail,
|
||||
username: "",
|
||||
date: `${new Date(data.month).getMonth() + 1}/${new Date(
|
||||
data.month
|
||||
).getFullYear()}`,
|
||||
date: `${month.toString().padStart(2, "0")}/${year}`,
|
||||
status: "Open",
|
||||
};
|
||||
console.log("req", reqData, data.month);
|
||||
|
|
@ -214,15 +222,10 @@ export default function EditMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import Swal from "sweetalert2";
|
|||
import withReactContent from "sweetalert2-react-content";
|
||||
import { error } from "@/config/swal";
|
||||
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
month: z.date({
|
||||
|
|
@ -42,6 +43,12 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function CreateMonthly() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -77,15 +84,15 @@ export default function CreateMonthly() {
|
|||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const month = new Date(data.month).getMonth() + 1;
|
||||
const year = new Date(data.month).getFullYear();
|
||||
const reqData = {
|
||||
planningTypeId: 2,
|
||||
title: data.title,
|
||||
time: "3",
|
||||
description: data.detail,
|
||||
username: "",
|
||||
date: `${new Date(data.month).getMonth() + 1}/${new Date(
|
||||
data.month
|
||||
).getFullYear()}`,
|
||||
date: `${month.toString().padStart(2, "0")}/${year}`,
|
||||
status: "Open",
|
||||
};
|
||||
console.log("req", reqData, data.month);
|
||||
|
|
@ -198,15 +205,10 @@ export default function CreateMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import {
|
|||
import dayjs from "dayjs";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
week: z.object({
|
||||
|
|
@ -61,7 +62,15 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
export default function DetailMonthly() {
|
||||
|
||||
const ViewEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/view-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function DetailWeekly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -227,16 +236,10 @@ export default function DetailMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
config={{ readonly: true }}
|
||||
/>
|
||||
<ViewEditor initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import {
|
|||
import dayjs from "dayjs";
|
||||
import { getPlanningById } from "@/service/planning/planning";
|
||||
import { useParams } from "next/navigation";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
week: z.object({
|
||||
|
|
@ -61,7 +62,13 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
export default function EditMonthly() {
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function EditWeekly() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -225,15 +232,10 @@ export default function EditMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const FormSchema = z.object({
|
||||
week: z.object({
|
||||
|
|
@ -61,6 +62,12 @@ const FormSchema = z.object({
|
|||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
export default function CreateMonthly() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -261,15 +268,10 @@ export default function CreateMonthly() {
|
|||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -20,144 +20,13 @@ import { useEffect, useState } from "react";
|
|||
import Cookies from "js-cookie";
|
||||
|
||||
export default function ExecutiveDashboard() {
|
||||
// const downloadReport = async () => {
|
||||
// // const formattedDate = `${reportDate.year}-${String(
|
||||
// // reportDate.month
|
||||
// // ).padStart(2, "0")}-${String(reportDate.day).padStart(2, "0")}`;
|
||||
// // const resLogin = await tableauSignin();
|
||||
// // const token = resLogin?.data.data?.credentials?.token;
|
||||
// // const resCover = await tableauViewImage(
|
||||
// // token,
|
||||
// // "1df3df4a-0457-4483-a8e1-160f70e7834f",
|
||||
// // formattedDate
|
||||
// // );
|
||||
// // const resTotalLink = await tableauViewImage(
|
||||
// // token,
|
||||
// // "8f902032-a6eb-4083-817a-57350f509b75",
|
||||
// // formattedDate
|
||||
// // );
|
||||
// // const resCount = await tableauViewImage(
|
||||
// // token,
|
||||
// // "11b2fe3c-f853-4156-800e-43342bf8e5ce",
|
||||
// // formattedDate
|
||||
// // );
|
||||
// // const resCoverCommentTwitter = await tableauViewImage(
|
||||
// // token,
|
||||
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
|
||||
// // formattedDate,
|
||||
// // 2
|
||||
// // );
|
||||
// // const resCoverCommentTiktok = await tableauViewImage(
|
||||
// // token,
|
||||
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
|
||||
// // formattedDate,
|
||||
// // 3
|
||||
// // );
|
||||
// // const resCoverCommentFacebook = await tableauViewImage(
|
||||
// // token,
|
||||
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
|
||||
// // formattedDate,
|
||||
// // 4
|
||||
// // );
|
||||
// // const resCoverCommentInstagram = await tableauViewImage(
|
||||
// // token,
|
||||
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
|
||||
// // formattedDate,
|
||||
// // 5
|
||||
// // );
|
||||
// // const resCoverCommentYoutube = await tableauViewImage(
|
||||
// // token,
|
||||
// // "28183e0b-80d0-428d-8684-2cbb572e97b3",
|
||||
// // formattedDate,
|
||||
// // 6
|
||||
// // );
|
||||
// // const resCommentTwitter = await tableauViewImage(
|
||||
// // token,
|
||||
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
|
||||
// // formattedDate,
|
||||
// // 2
|
||||
// // );
|
||||
// // const resCommentTiktok = await tableauViewImage(
|
||||
// // token,
|
||||
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
|
||||
// // formattedDate,
|
||||
// // 3
|
||||
// // );
|
||||
// // const resCommentFacebook = await tableauViewImage(
|
||||
// // token,
|
||||
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
|
||||
// // formattedDate,
|
||||
// // 4
|
||||
// // );
|
||||
// // const resCommentInstagram = await tableauViewImage(
|
||||
// // token,
|
||||
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
|
||||
// // formattedDate,
|
||||
// // 5
|
||||
// // );
|
||||
// // const resCommentYoutube = await tableauViewImage(
|
||||
// // token,
|
||||
// // "9a6d05ed-dea7-4a93-b709-82d0dab4790d",
|
||||
// // formattedDate,
|
||||
// // 6
|
||||
// // );
|
||||
// // const blobCover = new Blob([resCover.data], { type: "image/png" });
|
||||
// // const blobTotalLink = new Blob([resTotalLink.data], { type: "image/png" });
|
||||
// // const blobCount = new Blob([resCount.data], { type: "image/png" });
|
||||
// // const blobCoverCommentTwitter = new Blob([resCoverCommentTwitter.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCoverCommentTiktok = new Blob([resCoverCommentTiktok.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCoverCommentFacebook = new Blob([resCoverCommentFacebook.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCoverCommentInstagram = new Blob(
|
||||
// // [resCoverCommentInstagram.data],
|
||||
// // { type: "image/png" }
|
||||
// // );
|
||||
// // const blobCoverCommentYoutube = new Blob([resCoverCommentYoutube.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCommentTwitter = new Blob([resCommentTwitter.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCommentTiktok = new Blob([resCommentTiktok.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCommentFacebook = new Blob([resCommentFacebook.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCommentInstagram = new Blob([resCommentInstagram.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // const blobCommentYoutube = new Blob([resCommentYoutube.data], {
|
||||
// // type: "image/png",
|
||||
// // });
|
||||
// // await pdfGenerator([
|
||||
// // blobCover,
|
||||
// // blobTotalLink,
|
||||
// // blobCount,
|
||||
// // blobCoverCommentTwitter,
|
||||
// // blobCommentTwitter,
|
||||
// // blobCoverCommentTiktok,
|
||||
// // blobCommentTiktok,
|
||||
// // blobCoverCommentFacebook,
|
||||
// // blobCommentFacebook,
|
||||
// // blobCoverCommentInstagram,
|
||||
// // blobCommentInstagram,
|
||||
// // blobCoverCommentYoutube,
|
||||
// // blobCommentYoutube,
|
||||
// // ]);
|
||||
// };
|
||||
|
||||
const [startDate, setStartDate] = useState<any>(new Date());
|
||||
const [endDate, setEndDate] = useState<any>(new Date());
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
// const t = useTranslations("AnalyticsDashboard");
|
||||
const levelName = getCookiesDecrypt("ulnae");
|
||||
const poldaState = Cookies.get("state");
|
||||
const levelNumber = getCookiesDecrypt("ulne");
|
||||
const state = Cookies.get("state");
|
||||
const provState = Cookies.get("state-prov");
|
||||
|
||||
const [ticket1, setTicket1] = useState("");
|
||||
|
|
@ -171,42 +40,63 @@ export default function ExecutiveDashboard() {
|
|||
const baseUrl = "https://analytic.sitani.info/";
|
||||
const url = "https://analytic.sitani.info/trusted/";
|
||||
|
||||
const safeLevelName = levelNumber ?? "";
|
||||
|
||||
const view1 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?"
|
||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?"
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${provState}&`;
|
||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
|
||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`;
|
||||
|
||||
const view2 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-executive?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-publisher-polda-executive?provinsi-polda=${poldaState}&`;
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`;
|
||||
|
||||
const view3 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-executive?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?provinsi-polda=${poldaState}&`;
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`;
|
||||
|
||||
const view4 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?provinsi-polda=${poldaState}&`;
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?polda-selected=${state}&`;
|
||||
|
||||
const view5 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${poldaState}&`;
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const view6 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
@ -250,88 +140,107 @@ export default function ExecutiveDashboard() {
|
|||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="mt-3 flex flex-row gap-3 justify-center">
|
||||
<Card className="rounded-sm w-4/12 p-3">
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Polda
|
||||
</p>
|
||||
<LucideBoxSelect />
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket1 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view4 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket1}/${view4}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="rounded-sm w-4/12 p-3">
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Satker
|
||||
</p>
|
||||
<LucideBoxSelect />
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket2 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view4 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket2}/${view4}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="rounded-sm w-4/12 p-3">
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Polres
|
||||
</p>
|
||||
<LucideBoxSelect />
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket3 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view5 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket3}/${view5}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<div className="mt-3 flex gap-2 flex-row justify-center">
|
||||
{/* Polda */}
|
||||
{(levelNumber === "1" || levelNumber === "2") && (
|
||||
<Card
|
||||
className={`rounded-sm p-3 ${
|
||||
levelNumber === "2" ? "w-6/12" : "w-4/12"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Polda
|
||||
</p>
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket1 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view4 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${url + ticket1}/${view4}${param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Satker */}
|
||||
{(levelNumber === "1" || levelNumber === "3") && (
|
||||
<Card
|
||||
className={`rounded-sm p-3 ${
|
||||
levelNumber === "3" ? "w-full" : "w-4/12"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Satker
|
||||
</p>
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket2 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view6 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${url + ticket2}/${view6}${param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Polres */}
|
||||
{(levelNumber === "1" || levelNumber === "2") && (
|
||||
<Card
|
||||
className={`rounded-sm p-3 ${
|
||||
levelNumber === "2" ? "w-6/12" : "w-4/12"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Polres
|
||||
</p>
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket3 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view5 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${url + ticket3}/${view5}${param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full mt-3">
|
||||
<Card className="rounded-sm p-3 h-[750px]">
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">Konten Paling Populer</p>
|
||||
<LucideBoxSelect />
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket4 == "" ? (
|
||||
|
|
@ -354,14 +263,10 @@ export default function ExecutiveDashboard() {
|
|||
</div>
|
||||
<div className="w-full mt-3">
|
||||
<Card className="rounded-sm p-3 h-[750px]">
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="flex flex-row justify-between mx-3">
|
||||
<p className="text-base font-semibold">
|
||||
Heatmap Konten Dengan Interaksi
|
||||
Heatmap Konten dan Kategori dengan Interaksi
|
||||
</p>
|
||||
<p className="text-base font-semibold">
|
||||
Heatmap Kategori Dengan Interaksi
|
||||
</p>
|
||||
<LucideBoxSelect />
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket5 == "" ? (
|
||||
|
|
@ -386,7 +291,6 @@ export default function ExecutiveDashboard() {
|
|||
<Card className="rounded-sm p-3 h-auto">
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">Emergency Issue</p>
|
||||
<LucideBoxSelect />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
|
|
@ -39,6 +40,7 @@ import {
|
|||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
|
|
@ -177,32 +179,62 @@ const CollaborationTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<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="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex flex-row items-center gap-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="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex items-center">
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
|
|
@ -39,6 +40,7 @@ import {
|
|||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
|
|
@ -186,32 +188,62 @@ const EscalationTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<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="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex flex-row items-center gap-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="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex items-center">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
|
|
@ -40,6 +41,7 @@ import {
|
|||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
|
|
@ -184,32 +186,62 @@ const InternalTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<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="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex flex-row items-center gap-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="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex items-center">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import { listContest } from "@/service/contest/contest";
|
|||
import useTableColumns from "./columns";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
|
|
@ -172,8 +173,8 @@ const TaskTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<div className="mx-3">
|
||||
<div className="flex flex-row items-center gap-3">
|
||||
<div className="">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
|
|
@ -256,6 +257,34 @@ const TaskTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTaskTa from "@/components/form/task-ta/task-ta-form";
|
||||
import FormAskExpert from "@/components/form/shared/ask-expert-form";
|
||||
import FormDoItYourself from "@/components/form/shared/do-it-yourself-form";
|
||||
import FormAcceptAssignment from "@/components/form/shared/accept-assignment-form";
|
||||
|
||||
const AcceptAssignmentPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormAcceptAssignment />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AcceptAssignmentPage;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTaskTa from "@/components/form/task-ta/task-ta-form";
|
||||
import FormAskExpert from "@/components/form/shared/ask-expert-form";
|
||||
|
||||
const AskExpertCreatePage = () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormAskExpert />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AskExpertCreatePage;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTaskTa from "@/components/form/task-ta/task-ta-form";
|
||||
import FormAskExpert from "@/components/form/shared/ask-expert-form";
|
||||
import FormDoItYourself from "@/components/form/shared/do-it-yourself-form";
|
||||
|
||||
const DoItYourselfCreatePage = () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormDoItYourself />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DoItYourselfCreatePage;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
import { Link } from "@/components/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import {
|
||||
Carousel,
|
||||
|
|
@ -8,6 +9,7 @@ import {
|
|||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/components/ui/carousel";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { listCuratedContent } from "@/service/curated-content/curated-content";
|
||||
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
|
|
@ -26,6 +28,7 @@ type ImageData = {
|
|||
|
||||
const ImageSliderPage = () => {
|
||||
const router = useRouter();
|
||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||
const [imageData, setImageData] = useState<ImageData[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [limit] = useState(10);
|
||||
|
|
@ -88,6 +91,40 @@ const ImageSliderPage = () => {
|
|||
</div>
|
||||
</CardContent>
|
||||
</Link>
|
||||
{roleId === 11 && (
|
||||
<div className="flex flex-row justify-between mx-3 mb-3">
|
||||
<Link
|
||||
href={`/shared/curated-content/giat-routine/image/ask-the-expert/${image.id}`}
|
||||
>
|
||||
<Button color="primary" size="md">
|
||||
Ask The Expert
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/shared/curated-content/giat-routine/image/do-it-yourself/${image.id}`}
|
||||
>
|
||||
<Button color="primary" size="md">
|
||||
Do it Yourself
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{roleId === 12 && (
|
||||
<div className="mx-3 mb-3">
|
||||
<Link
|
||||
href={`/shared/curated-content/giat-routine/image/accept-assignment/${image.id}`}
|
||||
>
|
||||
<Button
|
||||
className="w-full "
|
||||
color="primary"
|
||||
size="md"
|
||||
>
|
||||
Accept Assignment
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
import { Link } from "@/components/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import {
|
||||
Carousel,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
"use client";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { MoreVertical } from "lucide-react";
|
||||
|
||||
const FeedbackForm = () => {
|
||||
const [region, setRegion] = useState("nasional");
|
||||
const [feedbackList, setFeedbackList] = useState<string[]>([
|
||||
"Silahkan berikan rating Anda terkait dengan kemudahan akses website MediaHUB Polri",
|
||||
"Silahkan berikan rating Anda terkait dengan konten MediaHUB Polri",
|
||||
"Silahkan berikan rating Anda terkait dengan tampilan MediaHUB Polri",
|
||||
]);
|
||||
const [newFeedback, setNewFeedback] = useState("");
|
||||
|
||||
const handleAddFeedback = () => {
|
||||
if (newFeedback.trim()) {
|
||||
setFeedbackList([...feedbackList, newFeedback.trim()]);
|
||||
setNewFeedback("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="bg-white rounded-md p-6 flex flex-col md:flex-row gap-6">
|
||||
<div className="md:w-1/2 space-y-4">
|
||||
<div>
|
||||
<p className="font-medium mb-2">Wilayah Publish</p>
|
||||
<RadioGroup
|
||||
defaultValue="nasional"
|
||||
onValueChange={(value) => setRegion(value)}
|
||||
className="flex items-center gap-6"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="nasional" id="nasional" />
|
||||
<label htmlFor="nasional">Nasional</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="internasional" id="internasional" />
|
||||
<label htmlFor="internasional">Internasional</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-medium mb-2">Poin Penilaian</p>
|
||||
<Textarea
|
||||
placeholder="Tulis poin yang ingin dijadikan penilaian"
|
||||
value={newFeedback}
|
||||
onChange={(e) => setNewFeedback(e.target.value)}
|
||||
className="min-h-[100px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleAddFeedback}
|
||||
className="bg-blue-600 text-white"
|
||||
>
|
||||
Tambah Feedback
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="md:w-1/2">
|
||||
<h3 className="font-semibold mb-2 border-b pb-1">Feedback</h3>
|
||||
<p className="font-medium mb-4">Pertanyaan</p>
|
||||
<ul className="space-y-3">
|
||||
{feedbackList.map((item, index) => (
|
||||
<li key={index} className="flex justify-between items-start">
|
||||
<span>{item}</span>
|
||||
<MoreVertical className="w-4 h-4 text-gray-600 cursor-pointer" />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeedbackForm;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FeedbackForm from "./components/feedback";
|
||||
|
||||
const FeedbackPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="bg-white rounded-md p-4 shadow-lg border">
|
||||
<h2 className="text-lg font-semibold">Feedback</h2>
|
||||
</div>
|
||||
<FeedbackForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeedbackPage;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { FaFacebookF, FaGoogle } from "react-icons/fa";
|
||||
|
||||
const SocialMediaPage = async () => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SiteBreadcrumb />
|
||||
<div className="bg-white rounded-md p-4 shadow-lg border">
|
||||
<h2 className="text-lg font-semibold">Social Media</h2>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-md p-6 shadow-lg border">
|
||||
<p className="text-base mb-6">Koneksi Social Media</p>
|
||||
<div className="flex flex-col md:flex-row justify-center items-center gap-10">
|
||||
<div className="flex flex-col items-center">
|
||||
<Button className="bg-[#3b5998] hover:bg-[#2d4373] text-white px-6 py-2 rounded-md flex items-center gap-2">
|
||||
<FaFacebookF className="text-lg" />
|
||||
Login With Facebook
|
||||
</Button>
|
||||
<p className="mt-2 text-sm text-gray-700">Tidak Terhubung</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<Button className="bg-[#ea4335] hover:bg-[#c73a2a] text-white px-6 py-2 rounded-md flex items-center gap-2">
|
||||
<FaGoogle className="text-lg" />
|
||||
Login With Google
|
||||
</Button>
|
||||
<p className="mt-2 text-sm text-gray-700">Tidak Terhubung</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SocialMediaPage;
|
||||
|
|
@ -582,7 +582,10 @@ const DetailInfo = () => {
|
|||
{formatDateToIndonesian(
|
||||
new Date(detailDataImage?.updatedAt)
|
||||
)}{" "}
|
||||
{"WIB"}
|
||||
{detailDataImage?.timezone
|
||||
? detailDataImage?.timezone
|
||||
: "WIB"}
|
||||
|
||||
</p>
|
||||
<p className="text-xs lg:text-sm flex justify-center items-center">
|
||||
|
|
||||
|
|
|
|||
|
|
@ -698,11 +698,12 @@ const FilterPage = () => {
|
|||
}}
|
||||
/>
|
||||
</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-1 mt-2">
|
||||
{formatDateToIndonesian(
|
||||
new Date(image?.createdAt)
|
||||
)}{" "}
|
||||
{image?.timezone ? image?.timezone : "WIB"}|{" "}
|
||||
{image?.timezone ? image?.timezone : "WIB"}
|
||||
|
|
||||
<Icon
|
||||
icon="formkit:eye"
|
||||
width="15"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import LoadScript from "@/utils/globals";
|
|||
|
||||
export const metadata: Metadata = {
|
||||
title: "Media Hub | POLRI",
|
||||
description: "created by codeshaper",
|
||||
description: "",
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
|
|
@ -33,15 +33,24 @@ export default async function RootLayout({
|
|||
<html lang={locale} dir={direction}>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" rel="stylesheet" />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.gstatic.com"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<LoadScript />
|
||||
</head>
|
||||
<body className={`${inter.className} dashcode-app`}>
|
||||
<NextIntlClientProvider messages={messages} locale={locale}>
|
||||
<AuthProvider>
|
||||
<ThemeProvider attribute="class" defaultTheme="light">
|
||||
<DirectionProvider direction={direction}>{children}</DirectionProvider>
|
||||
<DirectionProvider direction={direction}>
|
||||
{children}
|
||||
</DirectionProvider>
|
||||
<Toaster />
|
||||
<SonnerToaster />
|
||||
</ThemeProvider>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const Home = ({ params: { locale } }: { params: { locale: string } }) => {
|
|||
<NewContent group="mabes" type="latest" />
|
||||
<NewContent group="mabes" type="popular" />
|
||||
{/* <PopularContent /> */}
|
||||
<ContentCategory group="mabes" />
|
||||
<ContentCategory group="mabes" type="popular" />
|
||||
{/* <Coverage /> */}
|
||||
{/* <Division /> */}
|
||||
<AreaCoverageWorkUnits />
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ export default function FormBlogUpdate() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/blog");
|
||||
router.push("/in/contributor/blog");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ export default function FormBlog() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/blog");
|
||||
router.push("/in/contributor/blog");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -948,6 +948,8 @@ export default function FormAudio() {
|
|||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/audio/update-seo/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ export default function FormAudioUpdate() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/audio");
|
||||
router.push("/in/contributor/content/audio");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ export default function FormAudioSeo() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
router.push("/in/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import * as z from "zod";
|
|||
import { Upload } from "tus-js-client";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
import { redirect, useParams, useRouter } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -57,7 +57,7 @@ import dynamic from "next/dynamic";
|
|||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { request } from "http";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -85,6 +85,8 @@ export default function FormImage() {
|
|||
const router = useRouter();
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
||||
const t = useTranslations("Form");
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
|
|
@ -466,7 +468,7 @@ export default function FormImage() {
|
|||
tags: string;
|
||||
isYoutube: boolean;
|
||||
isInternationalMedia: boolean;
|
||||
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
|
||||
attachFromScheduleId?: number;
|
||||
} = {
|
||||
...data,
|
||||
title: finalTitle,
|
||||
|
|
@ -631,7 +633,7 @@ export default function FormImage() {
|
|||
setIsStartUpload(false);
|
||||
// hideProgress();
|
||||
Cookies.remove("idCreate");
|
||||
successSubmit("/in/contributor/content/image/");
|
||||
successSubmit("in/contributor/content/image/");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -951,18 +953,18 @@ export default function FormImage() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/image/update-seo/${selectedArticleId}`}
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
>
|
||||
{t("update")}
|
||||
</Button>
|
||||
</Link>
|
||||
{t("update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ export default function FormImageUpdate() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
router.push("/in/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ import {
|
|||
Legend,
|
||||
ChartOptions,
|
||||
} from "chart.js";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend);
|
||||
const imageSchema = z.object({
|
||||
|
|
@ -127,6 +129,7 @@ export default function FormImageSeo() {
|
|||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const t = useTranslations("Form");
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
|
|
@ -401,7 +404,7 @@ export default function FormImageSeo() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
router.push("/in/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -567,61 +570,174 @@ export default function FormImageSeo() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-5 mb-3">
|
||||
<Card>
|
||||
<Tabs defaultValue="content" className="">
|
||||
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Konten
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="checker"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Checker
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="content">
|
||||
{articleData !== undefined ? (
|
||||
<CardContent className="space-y-2 my-3">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="mx-5 mb-3">
|
||||
<Card className="p-0">
|
||||
<Tabs defaultValue="content" className="p-0">
|
||||
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Konten
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="checker"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Checker
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="content">
|
||||
{articleData !== undefined ? (
|
||||
<CardContent className="space-y-2 my-3">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="name">Judul</Label>
|
||||
<Input id="name" defaultValue={articleData?.title} />
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Main Keyword</Label>
|
||||
<Textarea
|
||||
id="mainKeyword"
|
||||
value={articleData?.mainKeyword}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Additional Keyword</Label>
|
||||
<Textarea
|
||||
id="additionalKeywords"
|
||||
value={articleData?.additionalKeywords}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Title</Label>
|
||||
<Textarea
|
||||
id="metaTitle"
|
||||
value={articleData?.metaTitle}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Description</Label>
|
||||
<Textarea
|
||||
id="metaDescription"
|
||||
value={articleData?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<Label>Article</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="checker">
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-start justify-start">
|
||||
<Pie
|
||||
data={data}
|
||||
options={options}
|
||||
className="text-left flex items-start justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="name">Judul</Label>
|
||||
<Input id="name" defaultValue={articleData?.title} />
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Main Keyword</Label>
|
||||
<Textarea
|
||||
id="mainKeyword"
|
||||
value={articleData?.mainKeyword}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Additional Keyword</Label>
|
||||
<Textarea
|
||||
id="additionalKeywords"
|
||||
value={articleData?.additionalKeywords}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Title</Label>
|
||||
<Textarea id="metaTitle" value={articleData?.metaTitle} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Description</Label>
|
||||
<Textarea
|
||||
id="metaDescription"
|
||||
value={articleData?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
<Accordion type="single" collapsible className="w-full ">
|
||||
<AccordionItem
|
||||
value="error"
|
||||
className="border border-red-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-red-600" />
|
||||
Errors ({errorSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{errorSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{errorSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No errors found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="warning"
|
||||
className="border border-yellow-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-yellow-600" />
|
||||
Warnings ({warningSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{warningSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{warningSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No warnings found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="optimized"
|
||||
className="border border-green-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-green-600" />
|
||||
Optimized ({optimizedSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{optimizedSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{optimizedSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No optimizations found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<Label>Article</Label>
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<Label>Article</Label>
|
||||
<Button size="md" className="bg-blue-500">
|
||||
Select Image From Content Bank
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
|
|
@ -639,119 +755,25 @@ export default function FormImageSeo() {
|
|||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="checker">
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-start justify-start">
|
||||
<Pie
|
||||
data={data}
|
||||
options={options}
|
||||
className="text-left flex items-start justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Accordion type="single" collapsible className="w-full ">
|
||||
<AccordionItem
|
||||
value="error"
|
||||
className="border border-red-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-red-600" />
|
||||
Errors ({errorSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{errorSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{errorSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No errors found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="warning"
|
||||
className="border border-yellow-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-yellow-600" />
|
||||
Warnings ({warningSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{warningSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{warningSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No warnings found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="optimized"
|
||||
className="border border-green-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-green-600" />
|
||||
Optimized ({optimizedSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{optimizedSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{optimizedSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No optimizations found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<Label>Article</Label>
|
||||
<Button size="md" className="bg-blue-500">
|
||||
Select Image From Content Bank
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
<div className="flex flex-row justify-star gap-3 mx-5">
|
||||
<div className="mb-3">
|
||||
<Link href={"/contributor/content/image"}>
|
||||
<Button type="submit" color="primary" variant="outline">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<Button type="submit" color="primary">
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -945,6 +945,8 @@ export default function FormTeks() {
|
|||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/teks/update-seo/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ export default function FormTeksUpdate() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/teks");
|
||||
router.push("/in/contributor/content/teks");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ export default function FormTeksSeo() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
router.push("/in/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -944,6 +944,8 @@ export default function FormVideo() {
|
|||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/video/update-seo/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ export default function PublishMediahub() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/planning/mediahub");
|
||||
router.push("/in/contributor/planning/mediahub");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ export default function PublishMedsos() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/planning/medsos-mediahub");
|
||||
router.push("/in/contributor/planning/medsos-mediahub");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,952 @@
|
|||
"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 { CalendarIcon, 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";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { addDays, format, setDate } from "date-fns";
|
||||
import { DateRange } from "react-day-picker";
|
||||
import TimePicker from "react-time-picker";
|
||||
import "react-time-picker/dist/TimePicker.css";
|
||||
import "react-clock/dist/Clock.css";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
|
||||
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 FormAskExpert() {
|
||||
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 [date, setDate] = React.useState<DateRange | undefined>({
|
||||
from: new Date(2024, 0, 1),
|
||||
});
|
||||
|
||||
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
semua: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
satker: false,
|
||||
});
|
||||
const [taskOutput, setTaskOutput] = useState({
|
||||
all: false,
|
||||
video: false,
|
||||
audio: false,
|
||||
image: false,
|
||||
text: 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 = [
|
||||
"komunikasi",
|
||||
"hukum",
|
||||
"bahasa",
|
||||
"ekonomi",
|
||||
"politik",
|
||||
"sosiologi",
|
||||
"ilmuadministrasipemerintah",
|
||||
"ti",
|
||||
].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);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailMedia(id);
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
}
|
||||
}
|
||||
initState();
|
||||
}, [id, refresh]);
|
||||
|
||||
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 fileTypeMapping = {
|
||||
all: "1",
|
||||
video: "2",
|
||||
audio: "4",
|
||||
image: "3",
|
||||
text: "5",
|
||||
};
|
||||
|
||||
const areasMapping = {
|
||||
semua: "0",
|
||||
komunikasi: "1",
|
||||
hukum: "2",
|
||||
bahasa: "3",
|
||||
ekonomi: "4",
|
||||
politik: "5",
|
||||
sosiologi: "6",
|
||||
ilmuadministrasipemerintah: "7",
|
||||
ti: "8",
|
||||
};
|
||||
|
||||
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(taskOutput)
|
||||
.filter((key) => taskOutput[key as keyof typeof taskOutput])
|
||||
.map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping])
|
||||
.join(",");
|
||||
|
||||
const selectedAreaExpert = Object.keys(expertise)
|
||||
.filter((key) => expertise[key as keyof typeof expertise])
|
||||
.map((key) => areasMapping[key as keyof typeof areasMapping])
|
||||
.join(",");
|
||||
|
||||
const requestData: {
|
||||
id?: number;
|
||||
title: string;
|
||||
assignedToLevel: any;
|
||||
assignedToUsers: any;
|
||||
assignmentTypeId: string;
|
||||
fileTypeOutput: string;
|
||||
areasExpertise: 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,
|
||||
areasExpertise: selectedAreaExpert,
|
||||
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);
|
||||
};
|
||||
|
||||
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-expert/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-ta");
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
{detail !== undefined ? (
|
||||
<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"
|
||||
defaultValue={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="mt-5">
|
||||
<Label>Ditujukan Kepada :</Label>
|
||||
</div>
|
||||
<div className=" 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="flex flex-col space-y-2 mt-5">
|
||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild className="px-0">
|
||||
<Button
|
||||
size="md"
|
||||
id="date"
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"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"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon size={15} className="mr-3" />
|
||||
{date?.from ? (
|
||||
date.to ? (
|
||||
<>
|
||||
{format(date.from, "LLL dd, y")} -{" "}
|
||||
{format(date.to, "LLL dd, y")}
|
||||
</>
|
||||
) : (
|
||||
format(date.from, "LLL dd, y")
|
||||
)
|
||||
) : (
|
||||
<span>Pick a date</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
initialFocus
|
||||
mode="range"
|
||||
defaultMonth={date?.from}
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
numberOfMonths={1}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("output-task")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(taskOutput).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={taskOutput[key as keyof typeof taskOutput]}
|
||||
onCheckedChange={(value) =>
|
||||
handleTaskOutputChange(
|
||||
key as keyof typeof taskOutput,
|
||||
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("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>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,263 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { z } from "zod";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
import { createSurveyData, getSurveyById } from "@/service/survey/survey";
|
||||
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
const surveySchema = z.object({
|
||||
accessFrequency: z.string(),
|
||||
uiExperienceDesign: z.string(),
|
||||
uiExperienceNavigation: z.string(),
|
||||
uiExperienceSpeed: z.string(),
|
||||
infoAccuracy: z.string(),
|
||||
infoCompleteness: z.string(),
|
||||
usefulness: z.string(),
|
||||
suggestion: z.string().optional(),
|
||||
});
|
||||
|
||||
type SurveySchema = z.infer<typeof surveySchema>;
|
||||
|
||||
export default function FormSurveyDetailPage() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [loadingSurvey, setLoadingSurvey] = useState(true);
|
||||
const [detail, setDetail] = useState<any>();
|
||||
const { id } = useParams() as { id: string };
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<SurveySchema>({
|
||||
resolver: zodResolver(surveySchema),
|
||||
defaultValues: {
|
||||
accessFrequency: "",
|
||||
uiExperienceDesign: "",
|
||||
uiExperienceNavigation: "",
|
||||
uiExperienceSpeed: "",
|
||||
infoAccuracy: "",
|
||||
infoCompleteness: "",
|
||||
usefulness: "",
|
||||
suggestion: "",
|
||||
},
|
||||
});
|
||||
|
||||
const options = {
|
||||
accessFrequency: [
|
||||
"Setiap hari",
|
||||
"Beberapa kali seminggu",
|
||||
"Beberapa kali dalam sebulan",
|
||||
"Baru pertama kali",
|
||||
],
|
||||
uiExperienceDesign: ["Sangat baik", "Baik", "Cukup", "Kurang", "Buruk"],
|
||||
uiExperienceNavigation: [
|
||||
"Sangat mudah",
|
||||
"Mudah",
|
||||
"Cukup",
|
||||
"Sulit",
|
||||
"Sangat sulit",
|
||||
],
|
||||
uiExperienceSpeed: [
|
||||
"Sangat cepat",
|
||||
"Cepat",
|
||||
"Cukup",
|
||||
"Lambat",
|
||||
"Sangat lambat",
|
||||
],
|
||||
infoAccuracy: ["Sangat puas", "Puas", "Cukup", "Kurang puas", "Tidak puas"],
|
||||
infoCompleteness: [
|
||||
"Sangat lengkap",
|
||||
"Lengkap",
|
||||
"Cukup",
|
||||
"Kurang lengkap",
|
||||
"Tidak lengkap",
|
||||
],
|
||||
usefulness: [
|
||||
"Sangat membantu",
|
||||
"Membantu",
|
||||
"Cukup membantu",
|
||||
"Kurang membantu",
|
||||
"Tidak membantu",
|
||||
],
|
||||
};
|
||||
|
||||
const renderControllerGroup = (
|
||||
name: keyof SurveySchema,
|
||||
question: string,
|
||||
choices: string[]
|
||||
) => (
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium">{question}</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{choices.map((choice, i) => (
|
||||
<Controller
|
||||
key={i}
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<label className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={field.value === choice}
|
||||
onCheckedChange={() => field.onChange(choice)}
|
||||
/>
|
||||
<span>{choice}</span>
|
||||
</label>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{errors[name] && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors[name]?.message as string}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const onSubmit = async (data: SurveySchema) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await createSurveyData(data);
|
||||
console.log("Survey submitted:", response);
|
||||
} catch (error) {
|
||||
console.error("Failed to submit survey:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSurvey = async () => {
|
||||
if (id) {
|
||||
setLoadingSurvey(true);
|
||||
try {
|
||||
const response = await getSurveyById(id);
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
|
||||
if (details) {
|
||||
setValue("accessFrequency", details.accessFrequency || "");
|
||||
setValue("uiExperienceDesign", details.uiExperienceDesign || "");
|
||||
setValue(
|
||||
"uiExperienceNavigation",
|
||||
details.uiExperienceNavigation || ""
|
||||
);
|
||||
setValue("uiExperienceSpeed", details.uiExperienceSpeed || "");
|
||||
setValue("infoAccuracy", details.infoAccuracy || "");
|
||||
setValue("infoCompleteness", details.infoCompleteness || "");
|
||||
setValue("usefulness", details.usefulness || "");
|
||||
setValue("suggestion", details.suggestion || "");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch survey detail:", error);
|
||||
} finally {
|
||||
setLoadingSurvey(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchSurvey();
|
||||
}, [id, setValue]);
|
||||
|
||||
if (loadingSurvey) {
|
||||
return <div className="p-6">Loading survey data...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full mx-auto p-6">
|
||||
<h1 className="text-2xl font-bold mb-2">
|
||||
SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI
|
||||
</h1>
|
||||
<p className="text-sm mb-6 text-gray-600">
|
||||
Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan
|
||||
kualitas layanan MediaHub Polri.
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||||
{renderControllerGroup(
|
||||
"accessFrequency",
|
||||
"1. Seberapa sering Anda mengakses MediaHub Polri?",
|
||||
options.accessFrequency
|
||||
)}
|
||||
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
2. Bagaimana pengalaman Anda dalam mengakses website ini?
|
||||
</p>
|
||||
<div className="space-y-3 mt-2">
|
||||
{renderControllerGroup(
|
||||
"uiExperienceDesign",
|
||||
"a) Tampilan dan desain website",
|
||||
options.uiExperienceDesign
|
||||
)}
|
||||
{renderControllerGroup(
|
||||
"uiExperienceNavigation",
|
||||
"b) Kemudahan navigasi",
|
||||
options.uiExperienceNavigation
|
||||
)}
|
||||
{renderControllerGroup(
|
||||
"uiExperienceSpeed",
|
||||
"c) Kecepatan akses website",
|
||||
options.uiExperienceSpeed
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub
|
||||
Polri?
|
||||
</p>
|
||||
<div className="space-y-3 mt-2">
|
||||
{renderControllerGroup(
|
||||
"infoAccuracy",
|
||||
"a) Akurat dan terpercaya",
|
||||
options.infoAccuracy
|
||||
)}
|
||||
{renderControllerGroup(
|
||||
"infoCompleteness",
|
||||
"b) Kelengkapan berita dan informasi",
|
||||
options.infoCompleteness
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderControllerGroup(
|
||||
"usefulness",
|
||||
"4. Apakah Anda merasa website ini membantu dalam mendapatkan informasi terkait Polri?",
|
||||
options.usefulness
|
||||
)}
|
||||
|
||||
<div>
|
||||
<p className="font-medium">5. Apa saran atau masukan Anda?</p>
|
||||
<Controller
|
||||
name="suggestion"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea
|
||||
placeholder="Tulis pesan Anda..."
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading ? "Mengirim..." : "Kirim"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -990,129 +990,7 @@ export default function FormTaskTaDetail() {
|
|||
<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-6 space-y-2">
|
||||
<Label>{t("assignment-selection")}</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedTarget}
|
||||
value={detail.assignedToRole}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</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-6 lg:pt-5 lg:ml-3">
|
||||
{Object.keys(unitSelection).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
disabled
|
||||
checked={
|
||||
unitSelection[key as keyof typeof unitSelection]
|
||||
}
|
||||
onCheckedChange={(value) =>
|
||||
setUnitSelection({ ...unitSelection, [key]: value })
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 lg:pt-5 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
|
||||
disabled
|
||||
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
|
||||
disabled
|
||||
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
|
||||
disabled
|
||||
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-6 space-y-2">
|
||||
<Label>{t("type-task")}</Label>
|
||||
<RadioGroup
|
||||
|
|
@ -1128,19 +1006,6 @@ export default function FormTaskTaDetail() {
|
|||
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
||||
</RadioGroup>
|
||||
</div> */}
|
||||
<div className="mt-6 space-y-2">
|
||||
<Label>{t("assigment-type")} </Label>
|
||||
<RadioGroup
|
||||
value={detail?.assignmentType}
|
||||
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">
|
||||
|
|
@ -1450,25 +1315,6 @@ export default function FormTaskTaDetail() {
|
|||
)}
|
||||
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||
{/* Display remaining time */}
|
||||
<div className="mt-4">
|
||||
<Label>Link Url</Label>
|
||||
{urlInputs.map((url: any, index: any) => (
|
||||
<div
|
||||
key={url.id}
|
||||
className="flex items-center gap-2 mt-2"
|
||||
>
|
||||
<input
|
||||
type="url"
|
||||
className="border rounded p-2 w-full"
|
||||
value={url}
|
||||
// onChange={(e) =>
|
||||
// handleLinkChange(index, e.target.value)
|
||||
// }
|
||||
placeholder={`Masukkan link berita ${index + 1}`}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -805,141 +805,6 @@ export default function FormTaskTaEdit() {
|
|||
<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>Tujuan Pemilihan Tugas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedTarget}
|
||||
value={detail.assignedToRole}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</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-6 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">
|
||||
[Kustom]
|
||||
</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-6 space-y-2">
|
||||
<Label>{t("assigment-type")} </Label>
|
||||
<RadioGroup
|
||||
defaultValue={detail?.assignmentType}
|
||||
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">
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import {
|
|||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { ChevronDown, ChevronUp, Trash2 } from "lucide-react";
|
||||
import { CalendarIcon, 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";
|
||||
|
|
@ -42,6 +42,18 @@ import { getCsrfToken } from "@/service/auth";
|
|||
import { loading } from "@/lib/swal";
|
||||
import { useTranslations } from "next-intl";
|
||||
import dynamic from "next/dynamic";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { addDays, format, setDate } from "date-fns";
|
||||
import { DateRange } from "react-day-picker";
|
||||
import TimePicker from "react-time-picker";
|
||||
import "react-time-picker/dist/TimePicker.css";
|
||||
import "react-clock/dist/Clock.css";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -134,6 +146,9 @@ export default function FormTaskTa() {
|
|||
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
|
||||
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
|
||||
const [voiceNoteLink, setVoiceNoteLink] = useState("");
|
||||
const [date, setDate] = React.useState<DateRange | undefined>({
|
||||
from: new Date(2024, 0, 1),
|
||||
});
|
||||
|
||||
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
|
|
@ -478,7 +493,7 @@ export default function FormTaskTa() {
|
|||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/assignment/file/upload`,
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
|
|
@ -548,7 +563,7 @@ export default function FormTaskTa() {
|
|||
isAudioUploadFinish &&
|
||||
isTextUploadFinish
|
||||
) {
|
||||
successSubmit("/in/contributor/task");
|
||||
successSubmit("/in/contributor/task-ta");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -735,6 +750,46 @@ export default function FormTaskTa() {
|
|||
<Label htmlFor="tugas-harian">Tugas Harian</Label>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-2 mt-5">
|
||||
<Label className="mr-3 mb-1">Tanggal</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild className="px-0">
|
||||
<Button
|
||||
size="md"
|
||||
id="date"
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"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"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon size={15} className="mr-3" />
|
||||
{date?.from ? (
|
||||
date.to ? (
|
||||
<>
|
||||
{format(date.from, "LLL dd, y")} -{" "}
|
||||
{format(date.to, "LLL dd, y")}
|
||||
</>
|
||||
) : (
|
||||
format(date.from, "LLL dd, y")
|
||||
)
|
||||
) : (
|
||||
<span>Pick a date</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
initialFocus
|
||||
mode="range"
|
||||
defaultMonth={date?.from}
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
numberOfMonths={1}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
|
|
@ -779,27 +834,6 @@ export default function FormTaskTa() {
|
|||
))}
|
||||
</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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,934 @@
|
|||
"use client";
|
||||
import React, {
|
||||
ChangeEvent,
|
||||
useEffect,
|
||||
useRef,
|
||||
Fragment,
|
||||
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 { Upload } from "tus-js-client";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { redirect, 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 { register } from "module";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import Cookies from "js-cookie";
|
||||
import {
|
||||
createMedia,
|
||||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
uploadThumbnail,
|
||||
} from "@/service/content/content";
|
||||
import { uploadThumbnailBlog } from "@/service/blog/blog";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
generateDataArticle,
|
||||
getDetailArticle,
|
||||
getGenerateKeywords,
|
||||
getGenerateTitle,
|
||||
} from "@/service/content/ai";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { CloudUpload, Trash2 } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { error, loading } from "@/config/swal";
|
||||
import { Item } from "@radix-ui/react-dropdown-menu";
|
||||
import { data } from "jquery";
|
||||
import { options } from "@fullcalendar/core/preact.js";
|
||||
import dynamic from "next/dynamic";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { request } from "http";
|
||||
import { useTranslations } from "next-intl";
|
||||
import FileUploader from "../shared/file-uploader";
|
||||
import { AudioRecorder } from "react-audio-voice-recorder";
|
||||
import { getTaskTa } from "@/service/task";
|
||||
import { taskDetail } from "./task-ta-form";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
}
|
||||
|
||||
type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type Option = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function FormTaskTaNew() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
const t = useTranslations("Form");
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [thumbnail, setThumbnail] = useState<File | null>(null);
|
||||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||
|
||||
const [selectedSEO, setSelectedSEO] = useState<string>("");
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||
const [editingArticleId, setEditingArticleId] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||
|
||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
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 [audioFile, setAudioFile] = useState<File | null>(null);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [links, setLinks] = useState<string[]>([""]);
|
||||
const [timer, setTimer] = useState<number>(120);
|
||||
const [fileTypeId, setFileTypeId] = useState<string>("");
|
||||
|
||||
const [content, setContent] = useState("");
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState("");
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
const [detail, setDetail] = useState<taskDetail>();
|
||||
const [refresh] = useState(false);
|
||||
|
||||
const options: Option[] = [
|
||||
{ id: "all", label: "SEMUA" },
|
||||
{ id: "5", label: "UMUM" },
|
||||
{ id: "6", label: "JOURNALIS" },
|
||||
{ id: "7", label: "POLRI" },
|
||||
{ id: "8", label: "KSP" },
|
||||
];
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
});
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
|
||||
.or(
|
||||
z.literal(articleBody || "").refine((val) => val.length > 0, {
|
||||
message: "Deskripsi diperlukan.",
|
||||
})
|
||||
),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
getValues,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
});
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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));
|
||||
};
|
||||
|
||||
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter" && e.currentTarget.value.trim()) {
|
||||
e.preventDefault();
|
||||
const newTag = e.currentTarget.value.trim();
|
||||
if (!tags.includes(newTag)) {
|
||||
setTags((prevTags) => [...prevTags, newTag]); // Add new tag
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = ""; // Clear input field
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveTag = (index: number) => {
|
||||
setTags((prevTags) => prevTags.filter((_, i) => i !== index)); // Remove tag
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getCategories();
|
||||
// setVideoActive(fileTypeId == '2');
|
||||
// getRoles();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const getCategories = async () => {
|
||||
try {
|
||||
const category = await listEnableCategory(fileTypeId);
|
||||
const resCategory: Category[] = category?.data.data.content;
|
||||
|
||||
setCategories(resCategory);
|
||||
console.log("data category", resCategory);
|
||||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
// setValue("categoryId", findCategory.id);
|
||||
setSelectedCategory(findCategory.id); // Set the selected category
|
||||
const response = await getTagsBySubCategoryId(findCategory.id);
|
||||
setTags(response?.data?.data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch categories:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (id: string): void => {
|
||||
if (id === "all") {
|
||||
if (publishedFor.includes("all")) {
|
||||
// Uncheck all checkboxes
|
||||
setPublishedFor([]);
|
||||
} else {
|
||||
// Select all checkboxes
|
||||
setPublishedFor(
|
||||
options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const updatedPublishedFor = publishedFor.includes(id)
|
||||
? publishedFor.filter((item) => item !== id)
|
||||
: [...publishedFor, id];
|
||||
|
||||
// Remove "all" if any checkbox is unchecked
|
||||
if (publishedFor.includes("all") && id !== "all") {
|
||||
setPublishedFor(updatedPublishedFor.filter((item) => item !== "all"));
|
||||
} else {
|
||||
setPublishedFor(updatedPublishedFor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
const response = await getTaskTa(id);
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
|
||||
// Add more state setting here based on other fields like broadcastType, taskOutput, etc.
|
||||
}
|
||||
}
|
||||
initState();
|
||||
}, [id, refresh]);
|
||||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
const finalDescription = articleBody || data.description;
|
||||
|
||||
if (!finalDescription.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
let requestData: {
|
||||
assignmentExpertId: any;
|
||||
title: string;
|
||||
description: string;
|
||||
htmlDescription: string;
|
||||
fileTypeId: string;
|
||||
categoryId: any;
|
||||
subCategoryId: any;
|
||||
uploadedBy: string;
|
||||
statusId: string;
|
||||
publishedFor: string;
|
||||
creatorName: string;
|
||||
tags: string;
|
||||
isYoutube: boolean;
|
||||
isInternationalMedia: boolean;
|
||||
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
|
||||
} = {
|
||||
...data,
|
||||
assignmentExpertId: detail?.id || null,
|
||||
title: finalTitle,
|
||||
description: finalDescription,
|
||||
htmlDescription: finalDescription,
|
||||
fileTypeId: fileTypeId,
|
||||
categoryId: selectedCategory,
|
||||
subCategoryId: selectedCategory,
|
||||
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
|
||||
statusId: "1",
|
||||
publishedFor: publishedFor.join(","),
|
||||
creatorName: data.creatorName,
|
||||
tags: finalTags,
|
||||
isYoutube: false,
|
||||
isInternationalMedia: false,
|
||||
};
|
||||
|
||||
let id = Cookies.get("idCreate");
|
||||
|
||||
if (scheduleId !== undefined) {
|
||||
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini
|
||||
}
|
||||
|
||||
if (id == undefined) {
|
||||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
Cookies.set("idCreate", response?.data?.data, { expires: 1 });
|
||||
id = response?.data?.data;
|
||||
|
||||
// Upload Thumbnail
|
||||
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: ImageSchema) => {
|
||||
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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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}/media/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
metadata: {
|
||||
mediaid: id,
|
||||
filename: file.name,
|
||||
contentType: file.type,
|
||||
fileTypeId: fileTypeId,
|
||||
duration,
|
||||
isWatermark: "true",
|
||||
},
|
||||
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-ta");
|
||||
}
|
||||
}
|
||||
|
||||
const successSubmit = (redirect: string) => {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
});
|
||||
};
|
||||
|
||||
// function successTodo() {
|
||||
// let counter = 0;
|
||||
// for (const element of progressInfo) {
|
||||
// if (element.percentage == 100) {
|
||||
// counter++;
|
||||
// }
|
||||
// }
|
||||
// if (counter == progressInfo.length) {
|
||||
// setIsStartUpload(false);
|
||||
// // hideProgress();
|
||||
// Cookies.remove("idCreate");
|
||||
// successSubmit("in/contributor/content/image/");
|
||||
// }
|
||||
// }
|
||||
|
||||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setThumbnail(file);
|
||||
console.log("Selected Thumbnail:", file);
|
||||
}
|
||||
if (file) {
|
||||
setPreview(URL.createObjectURL(file));
|
||||
}
|
||||
};
|
||||
|
||||
const renderFilePreview = (file: FileWithPreview) => {
|
||||
if (file.type.startsWith("image")) {
|
||||
return (
|
||||
<Image
|
||||
width={48}
|
||||
height={48}
|
||||
alt={file.name}
|
||||
src={URL.createObjectURL(file)}
|
||||
className=" rounded border p-0.5"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <Icon icon="tabler:file-description" />;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveFile = (file: FileWithPreview) => {
|
||||
const uploadedFiles = files;
|
||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
setFiles([...filtered]);
|
||||
};
|
||||
|
||||
const fileList = files.map((file) => (
|
||||
<div
|
||||
key={file.name}
|
||||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="file-preview">{renderFilePreview(file)}</div>
|
||||
<div>
|
||||
<div className=" text-sm text-card-foreground">{file.name}</div>
|
||||
<div className=" text-xs font-light text-muted-foreground">
|
||||
{Math.round(file.size / 100) / 10 > 1000 ? (
|
||||
<>{(Math.round(file.size / 100) / 10000).toFixed(1)}</>
|
||||
) : (
|
||||
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
|
||||
)}
|
||||
{" kb"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="icon"
|
||||
color="destructive"
|
||||
variant="outline"
|
||||
className=" border-none rounded-full"
|
||||
onClick={() => handleRemoveFile(file)}
|
||||
>
|
||||
<Icon icon="tabler:x" className=" h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
));
|
||||
|
||||
const handleRemoveAllFiles = () => {
|
||||
setFiles([]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Jika input title kosong, isi dengan hasil generate title
|
||||
if (!getValues("title") && title) {
|
||||
setValue("title", title);
|
||||
}
|
||||
}, [title, getValues, setValue]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{detail !== undefined ? (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col lg:flex-row gap-10">
|
||||
<Card className="w-full lg:w-8/12">
|
||||
<div className="px-6 py-6">
|
||||
<div className="gap-5 mb-5">
|
||||
{/* Input Title */}
|
||||
<div className="space-y-2 py-3">
|
||||
<Label>{t("title")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
defaultValue={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 gap-3">
|
||||
<div className=" space-y-2 w-3/12">
|
||||
<Label>{t("assigment-type")}</Label>
|
||||
<Select onValueChange={(val) => setFileTypeId(val)}>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Choose" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="2">Audio Visual</SelectItem>
|
||||
<SelectItem value="4">Audio</SelectItem>
|
||||
<SelectItem value="1">Image</SelectItem>
|
||||
<SelectItem value="3">Teks</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="py-3 space-y-2 w-9/12">
|
||||
<Label>{t("category")}</Label>
|
||||
<Select
|
||||
value={selectedCategory} // Ensure selectedTarget is updated correctly
|
||||
onValueChange={(id) => {
|
||||
console.log("Selected Category ID:", id);
|
||||
setSelectedCategory(id);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((category) => (
|
||||
<SelectItem
|
||||
key={category.id}
|
||||
value={category.id.toString()}
|
||||
>
|
||||
{category.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2.5 mt-5">
|
||||
<Label htmlFor="attachments">{t("attachment")}</Label>
|
||||
<div className="space-y-3">
|
||||
{fileTypeId === "2" && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{fileTypeId === "1" && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{fileTypeId === "3" && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{fileTypeId === "4" && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{fileTypeId === "4" &&
|
||||
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>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[500px]">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="creatorName"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.creatorName?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.creatorName.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3 space-y-2">
|
||||
<Label htmlFor="tags">{t("tags")}</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
placeholder="Add a tag and press Enter"
|
||||
onKeyDown={handleAddTag}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<div className="mt-3 ">
|
||||
{tags.map((tag, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className=" px-1 py-1 rounded-lg bg-black text-white mr-2 text-sm font-sans"
|
||||
>
|
||||
{tag}{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveTag(index)}
|
||||
className="remove-tag-button"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3">
|
||||
<div className="flex flex-col gap-3 space-y-2">
|
||||
<Label>{t("publish-target")}</Label>
|
||||
{options.map((option) => (
|
||||
<div key={option.id} className="flex gap-2 items-center">
|
||||
<Checkbox
|
||||
id={option.id}
|
||||
checked={
|
||||
option.id === "all"
|
||||
? publishedFor.length ===
|
||||
options.filter((opt: any) => opt.id !== "all")
|
||||
.length
|
||||
: publishedFor.includes(option.id)
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(option.id)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={option.id}>{option.label}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
<Button type="submit" color="primary">
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Link href={"/contributor/content/image"}>
|
||||
<Button type="submit" color="primary" variant="outline">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -692,7 +692,7 @@ export default function FormTaskEdit() {
|
|||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5">
|
||||
<div className="space-y-2">
|
||||
<Label>Judul</Label>
|
||||
<Label>{t("title")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
|
|
@ -712,7 +712,7 @@ export default function FormTaskEdit() {
|
|||
</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>Tujuan Pemilihan Tugas</Label>
|
||||
<Label>{t("assignment-selection")}</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedTarget}
|
||||
value={detail.assignedToRole}
|
||||
|
|
@ -752,7 +752,7 @@ export default function FormTaskEdit() {
|
|||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[Kustom]
|
||||
[{t("custom")}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
|
|
|
|||
|
|
@ -1,16 +1,40 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import Image from "next/image";
|
||||
import Coverage from "./coverage";
|
||||
import Division from "./division";
|
||||
|
||||
const AreaCoverageWorkUnits = () => {
|
||||
const [openPolda, setOpenPolda] = useState(false);
|
||||
const [openSatker, setOpenSatker] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (openPolda || openSatker) {
|
||||
document.body.style.overflow = "hidden";
|
||||
} else {
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
|
||||
// Cleanup on unmount
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [openPolda, openSatker]);
|
||||
return (
|
||||
<div className="mx-auto px-4 lg:px-12 py-6">
|
||||
<h2 className="text-center text-2xl font-bold text-gray-800 dark:text-white mb-4">
|
||||
Liputan <span className="text-[#bb3523]">Wilayah</span> & <span className="text-[#bb3523]">Satker</span>
|
||||
Liputan <span className="text-[#bb3523]">Wilayah</span> &{" "}
|
||||
<span className="text-[#bb3523]">Satker</span>
|
||||
</h2>
|
||||
<div className="h-1 w-[250px] bg-[#bb3523] mx-auto mb-6 rounded"></div>
|
||||
<div className="flex flex-col justify-center lg:flex-row gap-8">
|
||||
|
|
@ -43,9 +67,15 @@ const AreaCoverageWorkUnits = () => {
|
|||
</Dialog>
|
||||
|
||||
{/* SATKER */}
|
||||
<Dialog>
|
||||
<Dialog open={openSatker} onOpenChange={setOpenSatker}>
|
||||
<DialogTrigger className="flex flex-col gap-2 justify-center items-center shadow-lg group rounded-xl py-5 px-24 border-2 border-transparent hover:border-[#bb3523] transition-all duration-300">
|
||||
<Image width={1920} height={1080} alt="polri" src="/assets/logo-polri.png" className="h-32 w-32 group-hover:scale-110 group-hover:border-[#bb3523] transition-transform duration-300" />
|
||||
<Image
|
||||
width={1920}
|
||||
height={1080}
|
||||
alt="polri"
|
||||
src="/assets/logo-polri.png"
|
||||
className="h-32 w-32 group-hover:scale-110 group-hover:border-[#bb3523] transition-transform duration-300"
|
||||
/>
|
||||
<p className="text-base font-bold">Satuan Kerja Polri</p>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { getCategoryData, getPublicCategoryData } from "@/service/landing/landing";
|
||||
import {
|
||||
getCategoryData,
|
||||
getPublicCategoryData,
|
||||
} from "@/service/landing/landing";
|
||||
import Link from "next/link";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Button } from "../ui/button";
|
||||
|
|
@ -7,26 +10,45 @@ import { useTranslations } from "next-intl";
|
|||
import { usePathname } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "../ui/carousel";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "../ui/carousel";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const ContentCategory = (props: { group?: string }) => {
|
||||
const ContentCategory = (props: { group?: string; type: string }) => {
|
||||
const [categories, setCategories] = useState<any>();
|
||||
const t = useTranslations("LandingPage");
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
const [selectedTab, setSelectedTab] = useState("image");
|
||||
const poldaName = params?.polda_name;
|
||||
const satkerName = params?.satker_name;
|
||||
const router = useRouter();
|
||||
|
||||
let prefixPath = poldaName ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` : "/";
|
||||
let prefixPath = poldaName
|
||||
? `/polda/${poldaName}`
|
||||
: satkerName
|
||||
? `/satker/${satkerName}`
|
||||
: "/";
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, []);
|
||||
const initFetch = async () => {
|
||||
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
|
||||
);
|
||||
|
|
@ -52,7 +74,10 @@ const ContentCategory = (props: { group?: string }) => {
|
|||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||
</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 (
|
||||
<div className="px-4 lg:px-24 py-10">
|
||||
|
|
@ -60,12 +85,16 @@ const ContentCategory = (props: { group?: string }) => {
|
|||
<h2 className="text-center text-xl lg:text-3xl font-bold text-[#bb3523] mb-4">
|
||||
{pathname?.split("/")[1] == "in" ? (
|
||||
<>
|
||||
<span className="text-black dark:text-white">{t("category")} </span>
|
||||
<span className="text-black dark:text-white">
|
||||
{t("category")}
|
||||
</span>
|
||||
{t("content")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-black dark:text-white">{t("content")} </span>
|
||||
<span className="text-black dark:text-white">
|
||||
{t("content")}
|
||||
</span>
|
||||
{t("category")}
|
||||
</>
|
||||
)}
|
||||
|
|
@ -80,7 +109,9 @@ const ContentCategory = (props: { group?: string }) => {
|
|||
<div onClick={() => router.push(prefixPath + `all/filter?category=${category?.id}`)} className="cursor-pointer relative group rounded-md overflow-hidden shadow-md hover:shadow-lg block">
|
||||
{/* Gambar */}
|
||||
<Image
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
)}`}
|
||||
alt="category"
|
||||
width={2560}
|
||||
height={1440}
|
||||
|
|
@ -93,7 +124,9 @@ const ContentCategory = (props: { group?: string }) => {
|
|||
|
||||
{/* 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 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>
|
||||
</CarouselItem>
|
||||
|
|
@ -110,7 +143,11 @@ const ContentCategory = (props: { group?: string }) => {
|
|||
</div> */}
|
||||
<div className="flex items-center flex-row justify-center mt-7">
|
||||
<div
|
||||
// onClick={() => router.push(prefixPath + `/${selectedTab}/filter?sortBy=${props.type}`)}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
prefixPath + `/${selectedTab}/filter?sortBy=${props.type}`
|
||||
)
|
||||
}
|
||||
className="cursor-pointer border text-[#bb3523] rounded-lg text-sm lg:text-md px-4 py-1 border-[#bb3523]"
|
||||
>
|
||||
{t("seeAll")}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import { Autoplay, Pagination } from "swiper/modules";
|
|||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import "swiper/css";
|
||||
import "swiper/css/pagination";
|
||||
import FormSurvey from "./survey";
|
||||
|
||||
const HeroModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const [heroData, setHeroData] = useState<any>();
|
||||
|
|
@ -69,13 +70,21 @@ const HeroModal = ({ onClose }: { onClose: () => void }) => {
|
|||
initFetch();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.add("overflow-hidden");
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove("overflow-hidden");
|
||||
};
|
||||
}, []);
|
||||
|
||||
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="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 }}
|
||||
|
|
@ -87,7 +96,7 @@ const HeroModal = ({ onClose }: { onClose: () => void }) => {
|
|||
<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"
|
||||
className="absolute top-3 right-3 text-gray-700 dark:text-gray-300 hover:text-black dark:hover:text-white border border-white bg-white rounded-full h-8 w-8"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
|
@ -179,197 +188,6 @@ const SurveyIntroModal = ({ onNext }: { onNext: () => void }) => {
|
|||
);
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
|
|
@ -399,12 +217,29 @@ const Hero: React.FC = () => {
|
|||
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(() => {
|
||||
const roleId = Cookies.get("urie");
|
||||
const lastShown = Cookies.get("surveyLastShown");
|
||||
const now = new Date().getTime();
|
||||
|
||||
if (roleId && roleId !== "2") {
|
||||
const allowedRoles = ["1", "2", "3"];
|
||||
|
||||
if (roleId && allowedRoles.includes(roleId)) {
|
||||
if (!lastShown || now - parseInt(lastShown) > ONE_MONTH) {
|
||||
setShowSurveyModal(true);
|
||||
Cookies.set("surveyLastShown", now.toString(), { expires: 30 });
|
||||
|
|
@ -474,9 +309,7 @@ const Hero: React.FC = () => {
|
|||
/>
|
||||
)}
|
||||
|
||||
{showFormModal && (
|
||||
<SurveyFormModal onClose={() => setShowFormModal(false)} />
|
||||
)}
|
||||
{showFormModal && <FormSurvey />}
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<div className="flex flex-col space-y-3 mx-auto w-full lg:w-2/3">
|
||||
|
|
@ -504,7 +337,7 @@ const Hero: React.FC = () => {
|
|||
/>
|
||||
|
||||
<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>
|
||||
<div className="absolute top-0 left-0 bottom-0 w-1 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>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ const Navbar = () => {
|
|||
Cookies.remove(cookieName);
|
||||
});
|
||||
|
||||
router.push("/");
|
||||
window.location.href = "/";
|
||||
};
|
||||
|
||||
// const profilePicture = Cookies.get("profile_picture");
|
||||
|
|
@ -155,7 +155,7 @@ const Navbar = () => {
|
|||
|
||||
return (
|
||||
<div className="bg-[#f7f7f7] dark:bg-black shadow-md sticky top-0 z-50">
|
||||
<div className="flex items-center justify-between px-4 lg:px-20 py-4 gap-3">
|
||||
<div className="flex items-center justify-between px-4 lg:px-20 gap-3">
|
||||
<div className="flex flex-row gap-8">
|
||||
{/* Logo */}
|
||||
<Link href={prefixPath} className="flex items-center w-[150px] h-[120px]">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,235 @@
|
|||
"use client";
|
||||
|
||||
import { z } from "zod";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../ui/dialog";
|
||||
import { Checkbox } from "../ui/checkbox";
|
||||
import { Button } from "../ui/button";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { useState } from "react";
|
||||
import { createTaskTa } from "@/service/task";
|
||||
import { createSurveyData } from "@/service/survey/survey";
|
||||
|
||||
const surveySchema = z.object({
|
||||
accessFrequency: z.string(),
|
||||
uiExperienceDesign: z.string(),
|
||||
uiExperienceNavigation: z.string(),
|
||||
uiExperienceSpeed: z.string(),
|
||||
infoAccuracy: z.string(),
|
||||
infoCompleteness: z.string(),
|
||||
usefulness: z.string(),
|
||||
suggestion: z.string().optional(),
|
||||
});
|
||||
|
||||
type SurveySchema = z.infer<typeof surveySchema>;
|
||||
|
||||
export default function FormSurvey() {
|
||||
const [showSurvey, setShowSurvey] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<SurveySchema>({
|
||||
resolver: zodResolver(surveySchema),
|
||||
mode: "all",
|
||||
defaultValues: {
|
||||
accessFrequency: "",
|
||||
uiExperienceDesign: "",
|
||||
uiExperienceNavigation: "",
|
||||
uiExperienceSpeed: "",
|
||||
infoAccuracy: "",
|
||||
infoCompleteness: "",
|
||||
usefulness: "",
|
||||
suggestion: "",
|
||||
},
|
||||
});
|
||||
|
||||
const options = {
|
||||
accessFrequency: [
|
||||
"Setiap hari",
|
||||
"Beberapa kali seminggu",
|
||||
"Beberapa kali dalam sebulan",
|
||||
"Baru pertama kali",
|
||||
],
|
||||
uiExperienceDesign: ["Sangat baik", "Baik", "Cukup", "Kurang", "Buruk"],
|
||||
uiExperienceNavigation: [
|
||||
"Sangat mudah",
|
||||
"Mudah",
|
||||
"Cukup",
|
||||
"Sulit",
|
||||
"Sangat sulit",
|
||||
],
|
||||
uiExperienceSpeed: [
|
||||
"Sangat cepat",
|
||||
"Cepat",
|
||||
"Cukup",
|
||||
"Lambat",
|
||||
"Sangat lambat",
|
||||
],
|
||||
infoAccuracy: ["Sangat puas", "Puas", "Cukup", "Kurang puas", "Tidak puas"],
|
||||
infoCompleteness: [
|
||||
"Sangat lengkap",
|
||||
"Lengkap",
|
||||
"Cukup",
|
||||
"Kurang lengkap",
|
||||
"Tidak lengkap",
|
||||
],
|
||||
usefulness: [
|
||||
"Sangat membantu",
|
||||
"Membantu",
|
||||
"Cukup membantu",
|
||||
"Kurang membantu",
|
||||
"Tidak membantu",
|
||||
],
|
||||
};
|
||||
|
||||
const renderControllerGroup = (
|
||||
name: keyof SurveySchema,
|
||||
question: string,
|
||||
choices: string[]
|
||||
) => (
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium">{question}</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{choices.map((choice, i) => (
|
||||
<Controller
|
||||
key={i}
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<label className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={field.value === choice}
|
||||
onCheckedChange={() => field.onChange(choice)}
|
||||
/>
|
||||
<span>{choice}</span>
|
||||
</label>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{errors[name] && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors[name]?.message as string}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const onSubmit = async (data: SurveySchema) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await createSurveyData(data);
|
||||
console.log("API Response:", response);
|
||||
setShowSurvey(false);
|
||||
} catch (error) {
|
||||
console.error("Error submitting survey:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showSurvey} onOpenChange={setShowSurvey}>
|
||||
<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.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6 mt-4">
|
||||
{renderControllerGroup(
|
||||
"accessFrequency",
|
||||
"1. Seberapa sering Anda mengakses MediaHub Polri?",
|
||||
options.accessFrequency
|
||||
)}
|
||||
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
2. Bagaimana pengalaman Anda dalam mengakses website ini?
|
||||
</p>
|
||||
<div className="space-y-3 mt-2">
|
||||
{renderControllerGroup(
|
||||
"uiExperienceDesign",
|
||||
"a) Tampilan dan desain website",
|
||||
options.uiExperienceDesign
|
||||
)}
|
||||
{renderControllerGroup(
|
||||
"uiExperienceNavigation",
|
||||
"b) Kemudahan navigasi",
|
||||
options.uiExperienceNavigation
|
||||
)}
|
||||
{renderControllerGroup(
|
||||
"uiExperienceSpeed",
|
||||
"c) Kecepatan akses website",
|
||||
options.uiExperienceSpeed
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub
|
||||
Polri?
|
||||
</p>
|
||||
<div className="space-y-3 mt-2">
|
||||
{renderControllerGroup(
|
||||
"infoAccuracy",
|
||||
"a) Akurat dan terpercaya",
|
||||
options.infoAccuracy
|
||||
)}
|
||||
{renderControllerGroup(
|
||||
"infoCompleteness",
|
||||
"b) Kelengkapan berita dan informasi",
|
||||
options.infoCompleteness
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderControllerGroup(
|
||||
"usefulness",
|
||||
"4. Apakah Anda merasa website ini membantu dalam mendapatkan informasi terkait Polri?",
|
||||
options.usefulness
|
||||
)}
|
||||
|
||||
<div>
|
||||
<p className="font-medium">5. Apa saran atau masukan Anda?</p>
|
||||
<Controller
|
||||
name="suggestion"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea
|
||||
placeholder="Tulis pesan Anda..."
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant="outline" onClick={() => setShowSurvey(false)}>
|
||||
Batal
|
||||
</Button>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading ? "Mengirim..." : "Kirim"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -206,17 +206,20 @@ const LoginForm = () => {
|
|||
Number(profile?.data?.data?.roleId) == 18 ||
|
||||
Number(profile?.data?.data?.roleId) == 19
|
||||
) {
|
||||
if (profile?.data?.data?.roleId === 18) {
|
||||
if (
|
||||
profile?.data?.data?.roleId === 18 ||
|
||||
profile?.data?.data?.roleId === 2
|
||||
) {
|
||||
window.location.href = "/in/dashboard/executive";
|
||||
// router.push('/admin/dashboard');
|
||||
Cookies.set("status", "login", {
|
||||
expires: 1,
|
||||
});
|
||||
} else if (
|
||||
profile?.data?.data?.userLevel?.id == 761 ||
|
||||
profile?.data?.data?.userLevel?.id == 794 ||
|
||||
profile?.data?.data?.userLevel?.parentLevelId == 761
|
||||
) {
|
||||
window.location.href = "/in/dashboard/executive";
|
||||
window.location.href = "/in/dashboard";
|
||||
Cookies.set("status", "login", {
|
||||
expires: 1,
|
||||
});
|
||||
|
|
@ -261,8 +264,11 @@ const LoginForm = () => {
|
|||
} else if (msg == "Username & password valid") {
|
||||
onSubmit(data);
|
||||
} else {
|
||||
setStep(1);
|
||||
error("Username / password incorrect");
|
||||
}
|
||||
// else {
|
||||
// setStep(1);
|
||||
// }
|
||||
};
|
||||
|
||||
const handleSetupEmail = async () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default function ContentProductionVisualization() {
|
|||
const [hasMounted, setHasMounted] = useState(false);
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
const levelName = getCookiesDecrypt("ulnae");
|
||||
const poldaState = Cookies.get("state");
|
||||
const state = Cookies.get("state");
|
||||
const provState = Cookies.get("state-prov");
|
||||
|
||||
const [ticket1, setTicket1] = useState("");
|
||||
|
|
@ -21,28 +21,36 @@ export default function ContentProductionVisualization() {
|
|||
const baseUrl = "https://analytic.sitani.info/";
|
||||
const url = "https://analytic.sitani.info/trusted/";
|
||||
|
||||
const safeLevelName = levelName ?? "";
|
||||
|
||||
const view1 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-published-produksi?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-published-produksi-polda?provinsi-polda=${provState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda?polda-selected=${state}`;
|
||||
|
||||
const view2 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-publisher?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-publisher?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-publisher-polda?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-publisher?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-publisher-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-publisher-polda?polda-selected=${state}&`;
|
||||
|
||||
const view3 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-waktu-akses-pengguna?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-waktu-akses-pengguna-polda?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda?polda-selected=${state}`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda?polda-selected=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useTranslations } from "next-intl";
|
|||
|
||||
export default function DashboardVisualization() {
|
||||
const levelName = getCookiesDecrypt("ulnae");
|
||||
const poldaState = Cookies.get("state");
|
||||
const state = Cookies.get("state");
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
|
||||
const [ticket1, setTicket1] = useState("");
|
||||
|
|
@ -18,28 +18,36 @@ export default function DashboardVisualization() {
|
|||
const baseUrl = "https://analytic.sitani.info/";
|
||||
const url = "https://analytic.sitani.info/trusted/";
|
||||
|
||||
const safeLevelName = levelName ?? "";
|
||||
|
||||
const view1 =
|
||||
levelName == "MABES POLRI"
|
||||
safeLevelName === "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-content-monitor?"
|
||||
: "views/2023_09_MediaHUB-Viz-POLDA-content-monitor_Rev100/db-content-monitor?"
|
||||
: `views/2023_09_MediaHUB-Viz-ADMIN-POLDA-content-monitor_Rev100/db-content-monitor?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_09_MediaHUB-Viz-ADMIN-POLDA-content-monitor_Rev100/db-content-monitor?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_09_MediaHUB-Viz-ADMIN-POLDA-content-monitor_Rev100/db-content-monitor?provinsi-polda=${state}&`
|
||||
: `views/2023_09_MediaHUB-Viz-ADMIN-POLDA-content-monitor_Rev100/db-content-monitor?satker-selected=${state}&`;
|
||||
|
||||
const view2 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-content-interaction-konten?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-content-interaction-konten?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-content-interaction-konten-polda?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-konten-polda?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-konten-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-konten-polda?polda-selected=${state}&`;
|
||||
|
||||
const view3 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-penugasan?"
|
||||
: "views/2023_09_db-penugasan_rev100/db-penugasan?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-penugasan-polda?provinsi-polda=${poldaState}&`;
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`
|
||||
: `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ export default function ManagementUserVisualization() {
|
|||
const [ticket, setTicket] = useState("");
|
||||
const baseUrl = "https://analytic.sitani.info/";
|
||||
const url = "https://analytic.sitani.info/trusted/";
|
||||
const view = "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-user-count?:iid=5&";
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const view = "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-user-count?";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
const [isInternational, setIsInternational] = useState(false);
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default function PatternRelationVisualization() {
|
|||
const [hasMounted, setHasMounted] = useState(false);
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
const levelName = getCookiesDecrypt("ulnae");
|
||||
const poldaState = Cookies.get("state");
|
||||
const state = Cookies.get("state");
|
||||
const provState = Cookies.get("state-prov");
|
||||
|
||||
const [ticket1, setTicket1] = useState("");
|
||||
|
|
@ -22,35 +22,45 @@ export default function PatternRelationVisualization() {
|
|||
const baseUrl = "https://analytic.sitani.info/";
|
||||
const url = "https://analytic.sitani.info/trusted/";
|
||||
|
||||
const safeLevelName = levelName ?? "";
|
||||
|
||||
const view1 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-top10?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-top10?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-top10-polda?provinsi-polda=${provState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-top10?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-top10-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-top10-polda?polda-selected=${state}`;
|
||||
|
||||
const view2 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-polda?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-polda?polda-selected=${state}`;
|
||||
|
||||
const view3 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori-top10?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-kategori-top10?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-kategori-top10-polda?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10-polda?polda-selected=${state}&`;
|
||||
|
||||
const view4 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[3]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-kategori?"
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-kategori-polda?provinsi-polda=${poldaState}&`;
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default function PerformancePoldaViz() {
|
|||
const [hasMounted, setHasMounted] = useState(false);
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
const levelName = getCookiesDecrypt("ulnae");
|
||||
const poldaState = Cookies.get("state");
|
||||
const state = Cookies.get("state");
|
||||
const provState = Cookies.get("state-prov");
|
||||
|
||||
const [ticket1, setTicket1] = useState("");
|
||||
|
|
@ -22,21 +22,25 @@ export default function PerformancePoldaViz() {
|
|||
const baseUrl = "https://analytic.sitani.info/";
|
||||
const url = "https://analytic.sitani.info/trusted/";
|
||||
|
||||
const safeLevelName = levelName ?? "";
|
||||
|
||||
const view1 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-top10?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-ranking-polda?"
|
||||
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${provState}&`;
|
||||
: `views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${state}&`;
|
||||
|
||||
const view2 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-top10?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-top10?"
|
||||
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${poldaState}&`;
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${state}&`
|
||||
: `views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default function PerformancePolresViz() {
|
|||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-top10?"
|
||||
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${poldaState}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default function PerformanceSatkerViz() {
|
|||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev201/db-konten-top10?"
|
||||
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${provState}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
export const locales = ['en', 'in', 'ar'];
|
||||
export const locales = ["in", "en", "ar"];
|
||||
|
||||
export const baseURL = process.env.NEXT_PUBLIC_SITE_URL + "/api";
|
||||
export const baseURL = process.env.NEXT_PUBLIC_SITE_URL + "/api";
|
||||
|
|
|
|||
46
lib/menus.ts
46
lib/menus.ts
|
|
@ -2787,22 +2787,22 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
menus: [
|
||||
{
|
||||
id: "settings",
|
||||
href: "/supervisor/settings",
|
||||
href: "/supervisor/setting",
|
||||
label: t("settings"),
|
||||
active: pathname.includes("/settings"),
|
||||
active: pathname.includes("/setting"),
|
||||
icon: "uil:setting",
|
||||
submenus: [
|
||||
{
|
||||
href: "/settings/feedback",
|
||||
href: "/supervisor/setting/feedback",
|
||||
label: t("feedback"),
|
||||
active: pathname.includes("/settings/feedback"),
|
||||
active: pathname.includes("/supervisor/setting/feedback"),
|
||||
icon: "clarity:employee-group-line",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/settings/social-media",
|
||||
href: "/supervisor/setting/social-media",
|
||||
label: t("social-media"),
|
||||
active: pathname.includes("/settings/social-media"),
|
||||
active: pathname.includes("/supervisor/setting/social-media"),
|
||||
icon: "clarity:employee-group-line",
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -2811,7 +2811,11 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
],
|
||||
},
|
||||
];
|
||||
} else if (Number(roleId) == 11 || Number(roleId) == 19) {
|
||||
} else if (
|
||||
Number(roleId) == 11 ||
|
||||
Number(roleId) == 19 ||
|
||||
Number(roleId) == 12
|
||||
) {
|
||||
menusSelected = [
|
||||
{
|
||||
groupLabel: t("apps"),
|
||||
|
|
@ -3604,20 +3608,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "performance-polres",
|
||||
menus: [
|
||||
{
|
||||
id: "performance-polres",
|
||||
href: "/admin/performance-satker",
|
||||
label: t("performance-satker"),
|
||||
active: pathname.includes("/admin/performance-satker"),
|
||||
icon: "ant-design:signal-filled",
|
||||
submenus: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// groupLabel: "",
|
||||
// id: "performance-satker",
|
||||
// menus: [
|
||||
// {
|
||||
// id: "performance-polres",
|
||||
// href: "/admin/performance-satker",
|
||||
// label: t("performance-satker"),
|
||||
// active: pathname.includes("/admin/performance-satker"),
|
||||
// icon: "ant-design:signal-filled",
|
||||
// submenus: [],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "media-tracking",
|
||||
|
|
|
|||
|
|
@ -639,6 +639,8 @@
|
|||
"title": "Title",
|
||||
"category-name": "Category Name",
|
||||
"upload-date": "Upload Date",
|
||||
"generate-date": "Generate Date",
|
||||
"status": "status",
|
||||
"creator-group": "Creator Group",
|
||||
"source": "source",
|
||||
"published": "Published",
|
||||
|
|
|
|||
|
|
@ -640,6 +640,8 @@
|
|||
"title": "Judul",
|
||||
"category-name": "Nama Kategori",
|
||||
"upload-date": "Tanggal Upload",
|
||||
"generate-date": "Tanggal Generate",
|
||||
"status": "status",
|
||||
"creator-group": "Pembuat",
|
||||
"source": "Sumber",
|
||||
"published": "Diterbitkan",
|
||||
|
|
|
|||
|
|
@ -1,32 +1,25 @@
|
|||
import createMiddleware from 'next-intl/middleware';
|
||||
import {NextRequest, NextResponse} from 'next/server';
|
||||
import {locales} from '@/config';
|
||||
import createMiddleware from "next-intl/middleware";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { locales } from "@/config";
|
||||
|
||||
export default async function middleware(request: NextRequest) {
|
||||
|
||||
|
||||
|
||||
|
||||
// Step 1: Use the incoming request (example)
|
||||
const defaultLocale = request.headers.get('dashcode-locale') || 'en';
|
||||
|
||||
const defaultLocale = request.headers.get("dashcode-locale") || "in";
|
||||
|
||||
// Step 2: Create and call the next-intl middleware (example)
|
||||
const handleI18nRouting = createMiddleware({
|
||||
locales,
|
||||
defaultLocale
|
||||
|
||||
defaultLocale,
|
||||
});
|
||||
const response = handleI18nRouting(request);
|
||||
|
||||
|
||||
// Step 3: Alter the response (example)
|
||||
response.headers.set('dashcode-locale', defaultLocale);
|
||||
response.headers.set("dashcode-locale", defaultLocale);
|
||||
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
export const config = {
|
||||
// Match only internationalized pathnames
|
||||
matcher: ['/', '/(ar|en|in)/:path*']
|
||||
};
|
||||
matcher: ["/", "/(ar|in|en)/:path*"],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ const bundleAnalyzer = withBundleAnalyzer({
|
|||
});
|
||||
|
||||
const nextConfig = {
|
||||
// i18n: {
|
||||
// locales: ["en", "in"],
|
||||
// defaultLocale: "in",
|
||||
// },
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import {
|
||||
httpGetInterceptor,
|
||||
httpPostInterceptor,
|
||||
} from "../http-config/http-interceptor-service";
|
||||
|
||||
export async function createSurveyData(data: any) {
|
||||
const pathUrl = "users/satisfaction-survey";
|
||||
return httpPostInterceptor(pathUrl, data);
|
||||
}
|
||||
|
||||
export async function getSurveyData() {
|
||||
const url = `users/satisfaction-survey/pagination`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
||||
export async function getSurveyById(id: any) {
|
||||
const url = `users/satisfaction-survey?id=${id}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
Loading…
Reference in New Issue