fix conflict

This commit is contained in:
Sabda Yagra 2025-05-09 15:04:01 +07:00
commit 5fff155981
31 changed files with 6466 additions and 4184 deletions

View File

@ -32,6 +32,7 @@ import {
saveUserRolePlacements,
} from "@/service/management-user/management-user";
import { loading } from "@/config/swal";
import { Eye, EyeOff } from "lucide-react";
const FormSchema = z.object({
name: z.string({
@ -79,6 +80,14 @@ export default function AddExpertForm() {
const [userCompetencies, setUserCompetencies] = useState<any>();
const [userExperiences, setUserExperiences] = useState<any>();
const [userLevels, setUserLevels] = useState<any>();
const [passwordType, setPasswordType] = useState("password");
const [showPassword, setShowPassword] = useState(false);
const togglePasswordType = () => {
setPasswordType((prevType) =>
prevType === "password" ? "text" : "password"
);
};
const roleSelection = [
{
@ -115,7 +124,7 @@ export default function AddExpertForm() {
username: data.username,
email: data.email,
password: data.password,
adress: "",
address: "",
roleId: "EXP-ID",
phoneNumber: data.phoneNumber,
userCompetencyId: data.skills,
@ -308,12 +317,22 @@ export default function AddExpertForm() {
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<Input
type="password"
value={field.value}
placeholder="Masukkan Password"
onChange={field.onChange}
/>
<div className="relative">
<Input
value={field.value}
type={showPassword ? "text" : "password"}
placeholder="Masukkan Password"
onChange={field.onChange}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-default-500 hover:text-default-700"
tabIndex={-1}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
<FormMessage />
</FormItem>
)}

View File

@ -207,7 +207,7 @@ const MediaOnlineTable = () => {
</div>
<div className="flex justify-between ">
<Link href="/admin/broadcast/campaign-list">
<Link href="/admin/media-tracking/media-online/create">
<Button color="primary" size="md" className="text-sm mr-3">
Tambah Media Online
</Button>

View File

@ -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 FormMediaOnline from "@/components/form/media-tracking/media-tracking-form";
const MediaOnlineCreatePage = () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<FormMediaOnline />
</div>
</div>
);
};
export default MediaOnlineCreatePage;

View File

@ -43,16 +43,42 @@ const columns: ColumnDef<any>[] = [
header: "Tanggal",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
{
accessorKey: "title",
header: "Media Online",
cell: ({ row }) => <span>{row.getValue("title")}</span>,
},
{
accessorKey: "link",
header: "Link Berita",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
{
id: "actions",
accessorKey: "action",
header: "Aksi",
enableHiding: false,
cell: ({ row }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/admin/media-tracking/tb-news/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -66,6 +66,15 @@ import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link } from "@/i18n/routing";
import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { link } from "fs";
const NewsTable = () => {
const router = useRouter();
@ -80,6 +89,7 @@ const NewsTable = () => {
[]
);
const [showTable, setShowTable] = React.useState(false);
const [link, setLink] = React.useState("");
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
@ -204,100 +214,99 @@ const NewsTable = () => {
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3 border">
<div className="flex justify-between mb-10 items-center">
<div className="flex justify-between items-center">
<p className="text-xl font-medium text-default-900">
Tracking Berita hari ini!
</p>
</div>
<div>
<Label>
Masukan Link <span className="text-red-500">*</span>
</Label>
<Input></Input>
<p className="text-sm">Sisa kuota harian: 30 Artikel</p>
</div>
{!showTable && (
<div className="flex justify-end">
<Button
color="primary"
size="md"
className="text-sm mr-3"
variant="outline"
>
Batal
</Button>
<Button
color="primary"
size="md"
className="text-sm mr-3"
onClick={() => setShowTable(true)}
>
<Dialog>
<DialogTrigger asChild>
<Button className="bg-blue-600" size="md">
Tracking Berita
</Button>
</div>
)}
{showTable && (
<>
<Table className="overflow-hidden mt-4">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id} className="h-[75px]">
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
<div className="flex justify-end mt-4">
<Button
color="primary"
size="md"
className="text-sm mr-3"
onClick={() => setShowTable(false)}
>
Tracking Berita Baru
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Form Tracking Berita</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="space-y-2">
<Label htmlFor="link" className="text-sm font-medium">
Masukkan Link <span className="text-red-500">*</span>
</Label>
<Input
id="link"
placeholder="Masukkan Link disini!"
value={link}
onChange={(e) => setLink(e.target.value)}
/>
<p className="text-sm text-muted-foreground mt-1">
Sisa Kuota Harian: 5 Kata Kunci
</p>
</div>
</div>
</>
)}
<DialogFooter className="flex justify-end gap-2">
<Button variant="outline">Batal</Button>
<Button className="bg-blue-600 text-white">Tracking Berita</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Table className="overflow-hidden mt-4">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id} className="h-[75px]">
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
<div className="flex justify-end mt-4">
<Button
color="primary"
size="md"
className="text-sm mr-3"
onClick={() => setShowTable(false)}
>
Tracking Berita Baru
</Button>
</div>
{/* </>
)} */}
</div>
);
};

View File

@ -0,0 +1,12 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import NewsTable from "../../component/table";
import NewsDetailTable from "../component/table";
export default function DetailNews() {
return (
<div>
<SiteBreadcrumb />
<NewsDetailTable />
</div>
);
}

View File

@ -0,0 +1,53 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
formatDateToIndonesian,
getOnlyDate,
htmlToString,
} from "@/utils/globals";
import { Link, useRouter } from "@/i18n/routing";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "mediaOnline",
header: "Media Online",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
{
accessorKey: "link",
header: "Link Berita",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
];
export default columns;

View File

@ -0,0 +1,263 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Search,
SquarePen,
Trash2,
TrendingDown,
TrendingUp,
UserIcon,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import { paginationBlog } from "@/service/blog/blog";
import { ticketingPagination } from "@/service/ticketing/ticketing";
import { Badge } from "@/components/ui/badge";
import { useRouter, useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { listDataMedia } from "@/service/broadcast/broadcast";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link } from "@/i18n/routing";
import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { link } from "fs";
const NewsDetailTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [search, setSearch] = React.useState("");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [showTable, setShowTable] = React.useState(false);
const [link, setLink] = React.useState("");
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
});
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
let typingTimer: any;
const doneTypingInterval = 1500;
// const handleKeyUp = () => {
// clearTimeout(typingTimer);
// typingTimer = setTimeout(doneTyping, doneTypingInterval);
// };
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
// async function doneTyping() {
// fetchData();
// }
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
// React.useEffect(() => {
// fetchData();
// setPagination({
// pageIndex: 0,
// pageSize: Number(showData),
// });
// }, [page, showData]);
// async function fetchData() {
// try {
// loading();
// const res = await listDataMedia(
// page - 1,
// showData,
// search,
// categoryFilter?.sort().join(","),
// statusFilter?.sort().join(",")
// );
// const data = res?.data?.data;
// const contentData = data?.content;
// contentData.forEach((item: any, index: number) => {
// item.no = (page - 1) * Number(showData) + index + 1;
// });
// console.log("contentData : ", data);
// setDataTable(contentData);
// setTotalData(data?.totalElements);
// setTotalPage(data?.totalPages);
// close();
// } catch (error) {
// console.error("Error fetching tasks:", error);
// }
// }
React.useEffect(() => {
getCategories();
}, []);
async function getCategories() {
const category = await listEnableCategory("");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
const handleChange = (type: string, id: number, checked: boolean) => {
if (type === "category") {
if (checked) {
const temp: number[] = [...categoryFilter];
temp.push(id);
setCategoryFilter(temp);
} else {
const temp = categoryFilter.filter((a) => a !== id);
setCategoryFilter(temp);
}
} else {
if (checked) {
const temp: number[] = [...statusFilter];
temp.push(id);
setStatusFilter(temp);
} else {
const temp = statusFilter.filter((a) => a !== id);
setStatusFilter(temp);
}
}
};
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3 border">
<Table className="overflow-hidden mt-4">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id} className="h-[75px]">
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</div>
);
};
export default NewsDetailTable;

View File

@ -16,7 +16,7 @@ import { Link } from "@/components/navigation";
import { useRouter } from "next/navigation";
import { useToast } from "@/components/ui/use-toast";
import { deleteCategory } from "@/service/settings/settings";
import { deleteTask } from "@/service/task";
import { deleteTaskTa } from "@/service/task";
import { error, loading } from "@/lib/swal";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
@ -109,7 +109,7 @@ const useTableColumns = () => {
async function deleteProcess(id: any) {
loading();
const resDelete = await deleteTask(id);
const resDelete = await deleteTaskTa(id);
if (resDelete?.error) {
error(resDelete.message);
@ -159,18 +159,22 @@ const useTableColumns = () => {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/contributor/task-ta/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/task-ta/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
{roleId == 11 && (
<Link href={`/contributor/task-ta/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link>
)}
{roleId == 11 && (
<Link href={`/contributor/task-ta/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
)}
{roleId == 12 && (
<Link
href={`/contributor/task-ta/upload-task/${row.original.id}`}
@ -181,13 +185,15 @@ const useTableColumns = () => {
</DropdownMenuItem>
</Link>
)}
<DropdownMenuItem
onClick={() => TaskDelete(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
{roleId == 11 && (
<DropdownMenuItem
onClick={() => TaskDelete(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
);

View File

@ -13,7 +13,7 @@ const page = () => {
<WelcomeSatker />
<NewContent group="satker" type="latest" />
<NewContent group="satker" type="popular" />
<ContentCategory group="satker" />
<ContentCategory group="satker" type="latest" />
</div>
);
};

View File

@ -1,916 +1,48 @@
"use client";
import React, { useEffect, useState } from "react";
import {
checkWishlistStatus,
createPublicSuggestion,
deletePublicSuggestion,
deleteWishlist,
getDetail,
getDetailMetaData,
getPublicSuggestionList,
saveWishlist,
} from "@/service/landing/landing";
import { Metadata } from "next";
import DetailImage from "@/components/main/image-detail";
import DetailAudio from "@/components/main/audio-detail";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import React, { useEffect, useRef, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { Link, useRouter } from "@/i18n/routing";
import { Textarea } from "@/components/ui/textarea";
import { BarWave } from "react-cssfx-loading";
import { useToast } from "@/components/ui/use-toast";
import { checkWishlistStatus, createPublicSuggestion, deletePublicSuggestion, deleteWishlist, getDetail, getPublicSuggestionList, saveWishlist } from "@/service/landing/landing";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, error, loading, successCallback, warning } from "@/config/swal";
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import { checkMaliciousText, formatDateToIndonesian, getPublicLocaleTimestamp } from "@/utils/globals";
import parse from "html-react-parser";
import $ from "jquery";
import { useTranslations } from "next-intl";
import { postActivityLog } from "@/service/content/content";
interface Size {
label: string;
value: string;
}
const formWaveSurferOptions = (ref: any) => ({
container: ref,
waveColor: "#eee",
progressColor: "OrangeRed",
cursorColor: "OrangeRed",
barWidth: 3,
barRadius: 3,
responsive: true,
height: 150, // If true, normalize by the maximum peak instead of 1.0.
normalize: true, // Use the PeakCache to improve rendering speed of large waveforms.
partialRender: true,
});
const DetailAudio = () => {
const [selectedSize, setSelectedSize] = useState<string>("L");
const router = useRouter();
const pathname = usePathname();
const params = useParams();
const slug = String(params?.slug);
const [detailDataAudio, setDetailDataAudio] = useState<any>();
const [isSaved, setIsSaved] = useState(false);
const [wishlistId, setWishlistId] = useState();
const { toast } = useToast();
const [isDownloadAll, setIsDownloadAll] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [isFromSPIT, setIsFromSPIT] = useState(false);
const [main, setMain] = useState<any>();
const userId = getCookiesDecrypt("uie");
const [emailShareList, setEmailShareList] = useState<any>();
const [emailShareInput, setEmailShareInput] = useState<any>();
const [emailMessageInput, setEmailMessageInput] = useState();
const searchParams = useSearchParams();
const id = searchParams?.get("id");
const [width, setWidth] = useState<any>();
const [content, setContent] = useState<any>([]);
const userRoleId = getCookiesDecrypt("urie");
const [playing, setPlaying] = useState(false);
const wavesurfer = useRef<any>(null);
const waveformRef = useRef(null);
const [audioLoaded, setAudioLoaded] = useState(false);
const [volume, setVolume] = useState<any>(0.5);
const [message, setMessage] = useState("");
const [listSuggestion, setListSuggestion] = useState<any>();
const MySwal = withReactContent(Swal);
const [visibleInput, setVisibleInput] = useState<string | null>(null);
const t = useTranslations("LandingPage");
let typeString = "audio";
useEffect(() => {
initFetch();
checkWishlist();
sendActivityLog(2);
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailAudio", response);
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
setIsFromSPIT(response?.data?.data?.isFromSPIT);
setWidth(window.innerWidth);
setContent(response?.data?.data);
setListSuggestion(responseGet.data?.data);
setMain({
id: response?.data?.data?.files[0]?.id,
type: response?.data?.data?.fileType.name,
url:
Number(response?.data?.data?.fileType?.id) == 4
? response?.data?.data?.files[0]?.secondaryUrl
: Number(response?.data?.data?.fileType?.id) == 2
? `${process.env.NEXT_PUBLIC_API}/media/view?id=${response?.data?.data?.files[0]?.id}&operation=file&type=video`
: response?.data?.data?.files[0]?.url,
thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
names: response?.data?.data?.files[0]?.fileName,
format: response?.data?.data?.files[0]?.format,
widthPixel: response?.data?.data?.files[0]?.widthPixel,
heightPixel: response?.data?.data?.files[0]?.heightPixel,
size: response?.data?.data?.files[0]?.size,
caption: response?.data?.data?.files[0]?.caption,
});
setDetailDataAudio(response?.data?.data);
type Props = {
params: {
title: string;
slug: string;
description: string;
thumbnailLink: string;
};
const doBookmark = async () => {
if (userId) {
const data = {
mediaUploadId: slug?.split("-")?.[0],
};
loading();
const res = await saveWishlist(data);
if (res?.error) {
error(res.message);
return false;
}
close();
toast({
title: "Konten berhasil disimpan",
});
checkWishlist();
} else {
router.push("/auth");
}
};
async function checkWishlist() {
if (userId) {
const res = await checkWishlistStatus(slug.split("-")?.[0]);
console.log(res?.data?.data);
const isAlreadyOnWishlist = res?.data?.data !== "-1";
setWishlistId(res?.data?.data);
setIsSaved(isAlreadyOnWishlist);
}
}
const handleDeleteWishlist = async () => {
if (userId) {
loading();
const res = await deleteWishlist(wishlistId);
if (res?.error) {
error(res.message);
return false;
}
toast({
title: "Konten berhasil dihapus",
});
close();
checkWishlist();
} else {
router.push("/auth");
}
};
async function sendActivityLog(activityTypeId: number) {
const data = {
activityTypeId,
mediaId: slug.split("-")?.[0],
url: window.location.href,
};
// set activity
await postActivityLog(data);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} else {
sendActivityLog(2);
sendActivityLog(3);
if (isDownloadAll) {
let url: string;
const baseId = slug.split("-")?.[0];
// if (type === "1") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${resolutionSelected}`;
// } else if (type === "2") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${imageSizeSelected}`;
// } else {
url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}`;
// }
downloadFile(url, "FileDownload.zip");
} else {
if (isFromSPIT && main?.url?.includes("spit.humas")) {
downloadFile(`${main?.url}`, `${main.names}`);
} else {
downloadFile(`${main?.url}`, `${main.names}`);
}
}
// } else if (type === "1" && resolutionSelected?.length > 0) {
// if (isFromSPIT && main?.url?.includes("spit.humas")) {
// downloadFile(`${main?.url}`, `${main.names}`);
// } else {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
// downloadFile(url, `${main.names}`);
// }
// } else if (type === "2" && imageSizeSelected?.length > 0) {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=image&resolution=${imageSizeSelected}`;
// downloadFile(url, `${main.names}`);
// } else if (type === "3" || type === "4") {
// downloadFile(`${main?.url}`, `${main.names}`);
// }
}
}
};
const downloadFile = (fileUrl: string, name: string) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl, true);
xhr.responseType = "blob";
xhr.addEventListener("progress", (event) => {
if (event.lengthComputable) {
const percentCompleted = Math.round((event.loaded / event.total) * 100);
setDownloadProgress(percentCompleted);
}
});
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const contentType = xhr.getResponseHeader("content-type") || "application/octet-stream";
const extension = contentType.split("/")[1];
const filename = `${name}.${extension}`;
const blob = new Blob([xhr.response], {
type: contentType,
});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = filename;
document.body.append(a);
a.click();
a.remove();
}
});
xhr.onloadend = () => {
setDownloadProgress(0);
};
xhr.send();
};
const sizes = [
{ label: "MP3", value: "... MB" },
{ label: "WAV", value: "... MB" },
];
const handleShare = (type: any, url: any) => {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth/login");
} else {
sendActivityLog(2);
sendActivityLog(4);
if (type == "wa" && width <= 768) {
window.open(`whatsapp://send?${url}`, "_blank");
} else if (type == "wa" && width > 768) {
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
} else {
window.open(url);
}
}
};
async function shareToEmail() {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth/login");
} else {
const data = {
mediaUploadId: id?.split("-")?.[0],
email: emailShareList || [emailShareInput],
message: emailMessageInput,
url: window.location.href,
};
loading();
const res = await sendMediaUploadToEmail(data);
if (res?.error) {
error(res.message);
return false;
}
close();
successCallback("Konten Telah Dikirim");
}
}
const handlePlayPause = () => {
setPlaying(!playing);
wavesurfer?.current?.playPause();
};
const handleEmailList = (e: any) => {
const arrayEmail: any = [];
for (let i = 0; i < emailShareList?.length; i += 1) {
arrayEmail.push(emailShareList[i]);
}
if (e.which == 13) {
if (e.target.value) {
arrayEmail.push(e.target.value);
setEmailShareList(arrayEmail);
setEmailShareInput("");
}
e.preventDefault();
}
return false;
};
// useEffect(() => {
// function initState() {
// if (typeString == "audio" && main?.url != undefined) {
// const init = async () => {
// const { default: WaveSurfer } = await import("wavesurfer.js");
// setPlaying(false);
// const formatTime = function (time: any) {
// return [
// Math.floor((time % 3600) / 60),
// // minutes
// `00${Math.floor(time % 60)}`.slice(-2), // seconds
// ].join(":");
// };
// console.log("AUDIO", main?.url);
// const options = formWaveSurferOptions(waveformRef.current);
// wavesurfer.current = WaveSurfer.create(options);
// wavesurfer.current.load(main?.url);
// wavesurfer.current.on("ready", () => {
// // https://wavesurfer-js.org/docs/methods.html
// // wavesurfer.current.playPause();
// // setPlaying(true);
// setAudioLoaded(true);
// // make sure object stillavailable when file loaded
// if (wavesurfer.current) {
// wavesurfer.current.setVolume(volume);
// let volumeNow = volume;
// setVolume(volumeNow);
// }
// $(".waveform__duration").text(formatTime(wavesurfer.current.getDuration()));
// });
// // Show current time
// wavesurfer.current.on("audioprocess", () => {
// $(".waveform__counter").text(formatTime(wavesurfer.current.getCurrentTime()));
// });
// wavesurfer.current.on("finish", () => {
// setPlaying(false);
// });
// };
// init();
// // Removes events, elements and disconnects Web Audio nodes.
// // when component unmount
// return () => wavesurfer?.current?.destroy();
// }
// }
// initState();
// }, [main?.url]);
useEffect(() => {
if (typeString === "audio" && main?.url) {
const init = async () => {
const { default: WaveSurfer } = await import("wavesurfer.js");
if (wavesurfer.current) {
wavesurfer.current.destroy(); // 🔥 Hapus instance lama sebelum membuat yang baru
}
setPlaying(false);
const formatTime = function (time: any) {
return [
Math.floor((time % 3600) / 60),
// minutes
`00${Math.floor(time % 60)}`.slice(-2), // seconds
].join(":");
};
const options = formWaveSurferOptions(waveformRef.current);
wavesurfer.current = WaveSurfer.create(options);
wavesurfer.current.load(main?.url);
wavesurfer.current.on("ready", () => {
setAudioLoaded(true);
wavesurfer.current.setVolume(volume);
$(".waveform__duration").text(formatTime(wavesurfer.current.getDuration()));
});
wavesurfer.current.on("audioprocess", () => {
$(".waveform__counter").text(formatTime(wavesurfer.current.getCurrentTime()));
});
wavesurfer.current.on("finish", () => {
setPlaying(false);
});
};
init();
return () => {
if (wavesurfer.current) {
wavesurfer.current.destroy(); // 🔥 Hapus saat unmount
}
};
}
}, [main?.url]);
const onVolumeChange = (e: any) => {
const { target } = e;
const newVolume = +target?.value;
if (newVolume) {
setVolume(newVolume);
wavesurfer?.current?.setVolume(newVolume || 1);
}
};
async function sendSuggestionChild(parentId: any) {
const inputElement = document.querySelector(`#input-comment-${parentId}`) as HTMLInputElement;
if (inputElement && inputElement.value.length > 3) {
loading();
const data = {
mediaUploadId: slug?.split("-")?.[0],
message: inputElement.value,
parentId,
};
console.log(data);
const response = await createPublicSuggestion(data);
console.log(response);
const responseGet: any = await getPublicSuggestionList(slug?.split("-")?.[0]);
console.log(responseGet.data?.data);
setListSuggestion(responseGet.data?.data);
// Reset input field
inputElement.value = "";
// document.querySelector("#comment-id-" + parentId)?.style.display = "none";
close();
}
}
async function deleteDataSuggestion(dataId: any) {
loading();
const response = await deletePublicSuggestion(dataId);
console.log(response);
const responseGet = await getPublicSuggestionList(slug.split("-")?.[0]);
console.log(responseGet.data?.data);
setListSuggestion(responseGet.data?.data);
close();
}
const deleteData = (dataId: any) => {
MySwal.fire({
title: "Delete Comment",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Delete",
}).then((result: any) => {
if (result.isConfirmed) {
deleteDataSuggestion(dataId);
console.log(dataId);
}
});
};
const showInput = (e: any) => {
console.log(document.querySelector(`#${e}`)?.classList);
document.querySelector(`#${e}`)?.classList.toggle("none");
setVisibleInput(visibleInput === e ? null : e);
};
function addDefaultProfile(ev: any) {
ev.target.src = "/assets/avatar-profile.png";
}
async function sendSuggestionParent() {
if (message?.length > 3) {
loading();
const data = {
mediaUploadId: slug?.split("-")?.[0],
message,
parentId: null,
};
const response = await createPublicSuggestion(data);
console.log(response);
setMessage("");
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
console.log(responseGet?.data?.data);
setListSuggestion(responseGet?.data?.data);
// Hapus nilai semua input secara manual jika perlu
const inputs = document.querySelectorAll("input");
inputs.forEach((input) => {
input.value = "";
});
close();
}
}
const getInputValue = (e: any) => {
const message = e.target.value;
console.log(message);
setMessage(message);
};
const postData = () => {
const checkMessage = checkMaliciousText(message);
if (checkMessage == "") {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth");
} else {
sendSuggestionParent();
}
} else {
warning(checkMessage);
}
};
const postDataChild = (id: any) => {
const checkMessage = checkMaliciousText(message);
if (checkMessage == "") {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth");
} else {
sendSuggestionChild(id);
}
} else {
warning(checkMessage);
}
};
return (
<>
<div className="min-h-screen px-4 md:px-24 py-4">
{/* Container Utama */}
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
<div className="relative flex flex-row">
<button onClick={handlePlayPause}>
<img src={`/assets/${playing ? "stop-icon.png" : "play-icon.png"}`} />
</button>
<div
style={
audioLoaded
? {
display: "none",
}
: {}
}
>
<BarWave color="#ffc831" width="60px" height="25px" duration="2s" className="flex ml-5 mt-[50px] lg:ml-[200px]" />
</div>
<div id="waveform" ref={waveformRef} className="w-[350px] lg:w-[700px] ml-6 lg:ml-12"></div>
</div>
<div
className="flex flex-row mt-2"
style={
audioLoaded
? {}
: {
display: "none !important",
}
}
>
<Icon icon="fa:volume-up" />
{/* <Slider onChange={onVolumeChange} defaultValue={volume} /> */}
<input type="range" id="volume" name="volume" min="0.01" max="1" step=".025" onChange={onVolumeChange} defaultValue={volume} />
</div>
{/* Footer Informasi */}
<div className="text-gray-500 flex flex-col lg:flex-row justify-between items-center border-t mt-4">
<div className="flex flex-col lg:flex-row items-center mt-3 lg:justify-between">
<p className="text-xs lg:text-sm">
{t("by")}&nbsp;<span className="font-semibold text-black dark:text-white">{detailDataAudio?.uploadedBy?.userLevel?.name}</span>
</p>
{/* <p className="text-xs lg:text-sm">
&nbsp;|&nbsp;{t("updatedOn")} {detailDataAudio?.updatedAt} WIB &nbsp;|&nbsp;
</p> */}
<p className="text-xs lg:text-sm">
&nbsp;|&nbsp;{t("updatedOn")}&nbsp;
{formatDateToIndonesian(new Date(detailDataAudio?.updatedAt))} {"WIB"}
</p>
<p className="text-xs lg:text-sm flex justify-center items-center">
&nbsp;| &nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;{detailDataAudio?.clickCount}&nbsp;
</p>
</div>
<div className="mt-3">
<p className="flex text-end text-xs lg:text-sm font-semibold">
{t("creator")} {detailDataAudio?.creatorName}
</p>
</div>
</div>
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-lg lg:text-2xl my-8">{detailDataAudio?.title}</h1>
<div className="font-light text-justify mb-5 lg:mb-0 space-y-4" dangerouslySetInnerHTML={{ __html: detailDataAudio?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 h-fit bg-[#f7f7f7] dark:bg-slate-600 rounded-lg mx-4">
{isSaved ? (
<a onClick={() => handleDeleteWishlist()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark" width={40} />
<p className="text-base lg:text-lg">{t("delete")}</p>
</a>
) : (
<a onClick={() => doBookmark()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark-outline" width={40} />
<p className="text-base lg:text-lg">{t("save")}</p>
</a>
)}
{/* garis */}
<div className="border-t border-black my-4"></div>
<Link href={`/all/filter?title=polda&category=${detailDataAudio?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataAudio?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataAudio?.tags?.split(",").map((tag: string) => (
<a onClick={() => router.push(`/all/filter?tag=${tag}`)} key={tag} className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500">
{tag}
</a>
))}
</div>
<div className="border-t border-black my-4"></div>
{/* Opsi Ukuran Foto */}
<h4 className="flex text-lg justify-center items-center font-semibold my-3">{t("audioSize")}</h4>
<div className="border-t border-black my-4"></div>
<div className="space-y-2">
{sizes.map((size: any) => (
<div className="flex flex-row justify-between">
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
<div className="text-sm">{size.label}</div>
</div>
<div className="">
<div className="text-sm">{size.value}</div>
</div>
</div>
))}
</div>
{/* Download Semua */}
<div className="mt-4">
<label className="flex items-center space-x-2 text-sm">
<input type="checkbox" className="text-red-600 focus:ring-red-600" onChange={() => setIsDownloadAll(!isDownloadAll)} />
<span>{t("downloadAll")}</span>
</label>
</div>
{/* Tombol Download */}
<button onClick={handleDownload} className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="white" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
</svg>
{t("download")}
</button>
{/* Tombol Bagikan */}
<div className="flex flex-row py-3">
<p className="text-base font-semibold">{t("share")}</p>
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&quote=${content?.title}`)}>
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
</a>
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
</a>
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
</a>
<Popover>
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
</a>
</PopoverTrigger>
<PopoverContent>
<div className="flex flex-col">
<h1 className="mb-2">{t("shareTo")}</h1>
<div className="flex flex-col mb-2">
<p className="text-base font-semibold mb-1">{t("destinationEmail")}</p>
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder={t("pressEnter")} />
</div>
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
{t("send")}
</Button>
</div>
</PopoverContent>
</Popover>
</div>
</div>
</div>
</div>
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-4 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
<div className="gap-5 flex flex-col px-4 lg:px-14">
<p className="flex items-start text-lg">{t("comment")}</p>
<Textarea placeholder="Type your comments here." className="flex w-full pb-12" onChange={getInputValue} />
<button onClick={() => postData()} className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-4 py-1">
{t("send")}
</button>
</div>
<div className="border-b-2 border-slate-300 mt-4 w-auto"></div>
<div>
{listSuggestion?.map((data: any) => (
<div className="flex flex-col">
<div className="flex flex-row mt-2 px-4 lg:px-14">
<img src={data?.suggestionFrom?.profilePictureUrl} className="h-12 lg:h-16 w-12 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{Number(data.suggestionFrom?.roleId) == 2 || Number(data.suggestionFrom?.roleId) == 3 || Number(data.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : data.suggestionFrom?.fullname}
{getPublicLocaleTimestamp(new Date(data.createdAt))}
</p>
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{data?.message}</p>
<div>
<a
style={
Number(data.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${data.id}`)}
className="mr-2"
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
{Number(data.suggestionFrom?.id) == Number(userId) || Number(userRoleId) == 2 ? (
<a onClick={() => deleteData(data.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
) : (
""
)}
</div>
</div>
</div>
{visibleInput === `comment-id-${data.id}` && (
<div id={`comment-id-${data.id}`} className="px-4 pl-[72px] lg:px-14 lg:pl-32 mt-2 ">
<Textarea id={`input-comment-${data.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
<div className="flex flex-row gap-3">
<a onClick={() => postDataChild(data.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 mt-2 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${data.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg mt-2 w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
{data.children.length > 0
? data.children?.map((child1: any) => (
<div className="flex flex-col">
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-16 lg:pl-32">
<img src={child1.suggestionFrom?.profilePictureUrl} onError={addDefaultProfile} alt="" className="h-10 lg:h-16 w-10 lg:w-16 mr-2" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{" "}
<b>{Number(child1.suggestionFrom?.roleId) == 2 || Number(child1.suggestionFrom?.roleId) == 3 || Number(child1.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child1.suggestionFrom?.fullname}</b>{" "}
{getPublicLocaleTimestamp(new Date(child1.createdAt))}
</p>
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{parse(String(child1?.message))}</p>
<div>
<a
style={
Number(child1.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${child1.id}`)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
<a
style={
Number(child1.suggestionFrom?.id) == Number(userId)
? {}
: {
display: "none",
}
}
onClick={() => deleteData(child1.id)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
</div>
</div>
</div>
{visibleInput === `comment-id-${child1.id}` && (
<div id={`comment-id-${child1.id}`} className="px-4 lg:px-14 pl-28 lg:pl-[200px]">
<Textarea name="" className="mt-2 " id={`input-comment-${child1.id}`} placeholder={t("enterReply")} />
<div className="flex flex-row mt-2 gap-3">
<a onClick={() => postDataChild(child1.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${child1.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
{child1.children.length > 0
? child1.children?.map((child2: any) => (
<div className="">
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-28 lg:pl-48">
<img src={child2.suggestionFrom?.profilePictureUrl} className="h-9 lg:h-16 w-9 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{" "}
<b>{Number(child2.suggestionFrom?.roleId) == 2 || Number(child2.suggestionFrom?.roleId) == 3 || Number(child2.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child2.suggestionFrom?.fullname}</b>{" "}
{getPublicLocaleTimestamp(new Date(child2.createdAt))}
</p>
<p className="text-slate-500 text-sm mb-4">{parse(String(child2?.message))}</p>
<div>
<a
style={
Number(child2.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${child2.id}`)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
<a
style={
Number(child2.suggestionFrom?.id) == Number(userId)
? {}
: {
display: "none",
}
}
onClick={() => deleteData(child2.id)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
</div>
</div>
</div>
{visibleInput === `comment-id-${child2.id}` && (
<div id={`comment-id-${child2.id}`} className="px-4 lg:px-14 pl-40 lg:pl-[265px]">
<Textarea name="" id={`input-comment-${child2.id}`} className="my-2" placeholder="Masukkan balasan anda" />
<div className="flex flex-row gap-3">
<a onClick={() => postDataChild(child2.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${child2.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
</div>
))
: ""}
</div>
))
: ""}
</div>
))}
</div>
</div>
{/* Konten Serupa */}
<div className="px-4">
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailAudio;
export async function generateMetadata({ params }: any): Promise<Metadata> {
const slug = String(params?.slug);
const res = await getDetailMetaData(String(slug));
const audio = res?.data?.data;
// console.log("image", image);
return {
title: audio.title,
description: audio.description,
openGraph: {
title: audio?.title,
description: audio?.description,
audio: [`${audio?.thumbnailLink}`],
},
};
}
export default async function DetailInfo({ params }: Props) {
return <DetailAudio />;
}

View File

@ -1,766 +1,49 @@
"use client";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { Textarea } from "@/components/ui/textarea";
import { getCookiesDecrypt } from "@/lib/utils";
import { checkWishlistStatus, createPublicSuggestion, deletePublicSuggestion, deleteWishlist, getDetail, getPublicSuggestionList, saveWishlist } from "@/service/landing/landing";
import { close, error, loading, successCallback, warning } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { Link, useRouter } from "@/i18n/routing";
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { checkMaliciousText, formatDateToIndonesian, getPublicLocaleTimestamp } from "@/utils/globals";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import parse from "html-react-parser";
import { useTranslations } from "next-intl";
import { postActivityLog } from "@/service/content/content";
import {
checkWishlistStatus,
createPublicSuggestion,
deletePublicSuggestion,
deleteWishlist,
getDetail,
getDetailMetaData,
getPublicSuggestionList,
saveWishlist,
} from "@/service/landing/landing";
import { Metadata } from "next";
import DetailImage from "@/components/main/image-detail";
import DetailVideo from "@/components/main/video-detail";
import DetailDocument from "@/components/main/document-detail";
const DetailDocument = () => {
const [selectedSize, setSelectedSize] = useState<string>("L");
const [selectedTab, setSelectedTab] = useState("video");
const router = useRouter();
const pathname = usePathname();
const params = useParams();
const slug = String(params?.slug);
const [detailDataDocument, setDetailDataDocument] = useState<any>();
const [selectedDocument, setSelectedDocument] = useState(0);
const [isSaved, setIsSaved] = useState(false);
const [wishlistId, setWishlistId] = useState();
const { toast } = useToast();
const [isDownloadAll, setIsDownloadAll] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [isFromSPIT, setIsFromSPIT] = useState(false);
const [main, setMain] = useState<any>();
const [resolutionSelected, setResolutionSelected] = useState("720");
const [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
const [emailShareList, setEmailShareList] = useState<any>();
const [emailShareInput, setEmailShareInput] = useState<any>();
const [emailMessageInput, setEmailMessageInput] = useState();
const searchParams = useSearchParams();
const id = searchParams?.get("id");
const [width, setWidth] = useState<any>();
const [content, setContent] = useState<any>([]);
const userRoleId = getCookiesDecrypt("urie");
const [message, setMessage] = useState("");
const [listSuggestion, setListSuggestion] = useState<any>();
const MySwal = withReactContent(Swal);
const [visibleInput, setVisibleInput] = useState<string | null>(null);
const t = useTranslations("LandingPage");
interface Size {
label: string;
value: string;
}
let typeString = "document";
useEffect(() => {
initFetch();
checkWishlist();
sendActivityLog(2);
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailDocument", response);
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
setIsFromSPIT(response?.data?.data?.isFromSPIT);
setWidth(window.innerWidth);
setContent(response?.data.data);
setListSuggestion(responseGet.data?.data);
setMain({
id: response?.data?.data?.files[0]?.id,
type: response?.data?.data?.fileType.name,
url:
Number(response?.data?.data?.fileType?.id) == 4
? response?.data?.data?.files[0]?.secondaryUrl
: Number(response?.data?.data?.fileType?.id) == 2
? `${process.env.NEXT_PUBLIC_API}/media/view?id=${response?.data?.data?.files[0]?.id}&operation=file&type=video`
: response?.data?.data?.files[0]?.url,
thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
names: response?.data?.data?.files[0]?.fileName,
format: response?.data?.data?.files[0]?.format,
widthPixel: response?.data?.data?.files[0]?.widthPixel,
heightPixel: response?.data?.data?.files[0]?.heightPixel,
size: response?.data?.data?.files[0]?.size,
caption: response?.data?.data?.files[0]?.caption,
});
setDetailDataDocument(response?.data?.data);
type Props = {
params: {
title: string;
slug: string;
description: string;
thumbnailLink: string;
};
const doBookmark = async () => {
if (userId) {
const data = {
mediaUploadId: slug?.split("-")?.[0],
};
loading();
const res = await saveWishlist(data);
if (res?.error) {
error(res.message);
return false;
}
close();
toast({
title: "Konten berhasil disimpan",
});
checkWishlist();
} else {
router.push("/auth");
}
};
async function checkWishlist() {
if (userId) {
const res = await checkWishlistStatus(slug.split("-")?.[0]);
console.log(res?.data?.data);
const isAlreadyOnWishlist = res?.data?.data !== "-1";
setWishlistId(res?.data?.data);
setIsSaved(isAlreadyOnWishlist);
}
}
const handleDeleteWishlist = async () => {
if (userId) {
loading();
const res = await deleteWishlist(wishlistId);
if (res?.error) {
error(res.message);
return false;
}
toast({
title: "Konten berhasil dihapus",
});
close();
checkWishlist();
} else {
router.push("/auth");
}
};
const size = [{ label: "DOC" }, { label: "PPT" }, { label: "PDF" }];
function formatBytes(kb: any, decimals = 2) {
if (kb == 0 || kb == null) return "0 KB";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(kb) / Math.log(k));
return `${parseFloat((kb / k ** i).toFixed(dm))} ${sizes[i]}`;
}
async function sendActivityLog(activityTypeId: number) {
const data = {
activityTypeId,
mediaId: slug.split("-")?.[0],
url: window.location.href,
};
// set activity
await postActivityLog(data);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} else {
sendActivityLog(2);
sendActivityLog(3);
if (isDownloadAll) {
let url: string;
const baseId = slug.split("-")?.[0];
// if (type === "1") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${resolutionSelected}`;
// } else if (type === "2") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${imageSizeSelected}`;
// } else {
url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}`;
// }
downloadFile(url, "FileDownload.zip");
} else {
if (isFromSPIT && main?.url?.includes("spit.humas")) {
downloadFile(`${main?.url}`, `${main.names}`);
} else {
downloadFile(`${main?.url}`, `${main.names}`);
}
}
// } else if (type === "1" && resolutionSelected?.length > 0) {
// if (isFromSPIT && main?.url?.includes("spit.humas")) {
// downloadFile(`${main?.url}`, `${main.names}`);
// } else {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
// downloadFile(url, `${main.names}`);
// }
// } else if (type === "2" && imageSizeSelected?.length > 0) {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=image&resolution=${imageSizeSelected}`;
// downloadFile(url, `${main.names}`);
// } else if (type === "3" || type === "4") {
// downloadFile(`${main?.url}`, `${main.names}`);
// }
}
}
};
const downloadFile = (fileUrl: string, name: string) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl, true);
xhr.responseType = "blob";
xhr.addEventListener("progress", (event) => {
if (event.lengthComputable) {
const percentCompleted = Math.round((event.loaded / event.total) * 100);
setDownloadProgress(percentCompleted);
}
});
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const contentType = xhr.getResponseHeader("content-type") || "application/octet-stream";
const extension = contentType.split("/")[1];
const filename = `${name}.${extension}`;
const blob = new Blob([xhr.response], {
type: contentType,
});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = filename;
document.body.append(a);
a.click();
a.remove();
}
});
xhr.onloadend = () => {
setDownloadProgress(0);
};
xhr.send();
};
const handleShare = (type: any, url: any) => {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth/login");
} else {
sendActivityLog(2);
sendActivityLog(4);
if (type == "wa" && width <= 768) {
window.open(`whatsapp://send?${url}`, "_blank");
} else if (type == "wa" && width > 768) {
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
} else {
window.open(url);
}
}
};
async function shareToEmail() {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth/login");
} else {
const data = {
mediaUploadId: id?.split("-")?.[0],
email: emailShareList || [emailShareInput],
message: emailMessageInput,
url: window.location.href,
};
loading();
const res = await sendMediaUploadToEmail(data);
if (res?.error) {
error(res.message);
return false;
}
close();
successCallback("Konten Telah Dikirim");
}
}
const handleEmailList = (e: any) => {
const arrayEmail: any = [];
for (let i = 0; i < emailShareList?.length; i += 1) {
arrayEmail.push(emailShareList[i]);
}
if (e.which == 13) {
if (e.target.value) {
arrayEmail.push(e.target.value);
setEmailShareList(arrayEmail);
setEmailShareInput("");
}
e.preventDefault();
}
return false;
};
async function sendSuggestionChild(parentId: any) {
const inputElement = document.querySelector(`#input-comment-${parentId}`) as HTMLInputElement;
if (inputElement && inputElement.value.length > 3) {
loading();
const data = {
mediaUploadId: slug?.split("-")?.[0],
message: inputElement.value,
parentId,
};
console.log(data);
const response = await createPublicSuggestion(data);
console.log(response);
const responseGet: any = await getPublicSuggestionList(slug?.split("-")?.[0]);
console.log(responseGet.data?.data);
setListSuggestion(responseGet.data?.data);
// Reset input field
inputElement.value = "";
// document.querySelector("#comment-id-" + parentId)?.style.display = "none";
close();
}
}
async function deleteDataSuggestion(dataId: any) {
loading();
const response = await deletePublicSuggestion(dataId);
console.log(response);
const responseGet = await getPublicSuggestionList(slug.split("-")?.[0]);
console.log(responseGet.data?.data);
setListSuggestion(responseGet.data?.data);
close();
}
const deleteData = (dataId: any) => {
MySwal.fire({
title: "Delete Comment",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Delete",
}).then((result: any) => {
if (result.isConfirmed) {
deleteDataSuggestion(dataId);
console.log(dataId);
}
});
};
const showInput = (e: any) => {
console.log(document.querySelector(`#${e}`)?.classList);
document.querySelector(`#${e}`)?.classList.toggle("none");
setVisibleInput(visibleInput === e ? null : e);
};
function addDefaultProfile(ev: any) {
ev.target.src = "/assets/avatar-profile.png";
}
async function sendSuggestionParent() {
if (message?.length > 3) {
loading();
const data = {
mediaUploadId: slug?.split("-")?.[0],
message,
parentId: null,
};
const response = await createPublicSuggestion(data);
console.log(response);
setMessage("");
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
console.log(responseGet?.data?.data);
setListSuggestion(responseGet?.data?.data);
// Hapus nilai semua input secara manual jika perlu
const inputs = document.querySelectorAll("input");
inputs.forEach((input) => {
input.value = "";
});
close();
}
}
const getInputValue = (e: any) => {
const message = e.target.value;
console.log(message);
setMessage(message);
};
const postData = () => {
const checkMessage = checkMaliciousText(message);
if (checkMessage == "") {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth");
} else {
sendSuggestionParent();
}
} else {
warning(checkMessage);
}
};
const postDataChild = (id: any) => {
const checkMessage = checkMaliciousText(message);
if (checkMessage == "") {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth");
} else {
sendSuggestionChild(id);
}
} else {
warning(checkMessage);
}
};
return (
<>
<div className="px-4 md:px-24 py-4">
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
<div className="relative">
<img src={detailDataDocument?.files[selectedDocument]?.url} alt="Main" className="rounded-lg w-auto h-fit" />
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="text-gray-500 flex flex-col lg:flex-row justify-between items-center border-t mt-4">
{/* <p className="flex flex-row items-center mt-3">
oleh&nbsp;<span className="font-semibold text-black">{detailDataDocument?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataDocument?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataDocument?.clickCount}
</p>
<p className="mt-3">Kreator: {detailDataDocument?.creatorName}</p> */}
<div className="flex flex-col lg:flex-row items-center mt-3 lg:justify-between">
<p className="text-xs lg:text-sm">
{t("by")}&nbsp;<span className="font-semibold text-black dark:text-white">{detailDataDocument?.uploadedBy?.userLevel?.name}</span>
</p>
{/* <p className="text-xs lg:text-sm">
&nbsp;|&nbsp; {t("updatedOn")} {detailDataDocument?.updatedAt} WIB &nbsp;|&nbsp;
</p> */}
<p className="text-xs lg:text-sm">
&nbsp;|&nbsp;{t("updatedOn")}&nbsp;
{formatDateToIndonesian(new Date(detailDataDocument?.updatedAt))} {"WIB"}
</p>
<p className="text-xs lg:text-sm flex justify-center items-center">
&nbsp;| &nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;{detailDataDocument?.clickCount}&nbsp;
</p>
</div>
<div className="mt-3">
<p className="flex text-end text-xs lg:text-sm font-semibold">
{t("creator")}
{detailDataDocument?.creatorName}
</p>
</div>
</div>
{/* Keterangan */}
<div className="">
<h1 className="flex flex-row font-bold text-lg lg:text-2xl my-8 text-justify">{detailDataDocument?.title}</h1>
<div className="font-light text-justify mb-5 space-y-4 lg:mb-0" dangerouslySetInnerHTML={{ __html: detailDataDocument?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] dark:bg-slate-600 h-fit rounded-lg mx-4">
{isSaved ? (
<a onClick={() => handleDeleteWishlist()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark" width={40} />
<p className="text-base lg:text-lg">{t("delete")}</p>
</a>
) : (
<a onClick={() => doBookmark()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark-outline" width={40} />
<p className="text-base lg:text-lg">{t("save")}</p>
</a>
)}
{/* garis */}
<div className="border-t border-black my-4"></div>
<Link href={`/all/filter?title=polda&category=${detailDataDocument?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataDocument?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataDocument?.tags?.split(",").map((tag: string) => (
<a onClick={() => router.push(`/all/filter?tag=${tag}`)} key={tag} className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500">
{tag}
</a>
))}
</div>
<div className="border-t border-black my-4"></div>
{/* Opsi Ukuran Foto */}
<h4 className="flex text-lg justify-center items-center font-semibold my-3">{t("docSize")}</h4>
<div className="border-t border-black my-4"></div>
<div className="space-y-2">
{size.map((size: any) => (
<div className="flex flex-row justify-between">
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
<div className="text-sm">{size.label}</div>
</div>
<div className="">
<div className="text-sm">{formatBytes(size?.size)}</div>
</div>
</div>
))}
</div>
{/* Download Semua */}
<div className="mt-4">
<label className="flex items-center space-x-2 text-sm">
<input type="checkbox" className="text-red-600 focus:ring-red-600" onChange={() => setIsDownloadAll(!isDownloadAll)} />
<span>{t("downloadAll")}</span>
</label>
</div>
{/* Tombol Download */}
<button onClick={handleDownload} className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="white" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
</svg>
{t("download")}
</button>
{/* Tombol Bagikan */}
<div className="flex flex-row py-3">
<p className="text-base font-semibold">{t("share")}</p>
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&quote=${content?.title}`)}>
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
</a>
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
</a>
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
</a>
<Popover>
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
</a>
</PopoverTrigger>
<PopoverContent>
<div className="flex flex-col">
<h1 className="mb-2">{t("shareTo")}</h1>
<div className="flex flex-col mb-2">
<p className="text-base font-semibold mb-1">{t("destinationEmail")}</p>
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder={t("pressEnter")} />
</div>
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
{t("send")}
</Button>
</div>
</PopoverContent>
</Popover>
</div>
</div>
</div>
</div>
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-4 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
<div className="gap-5 flex flex-col px-4 lg:px-14">
<p className="flex items-start text-lg">{t("comment")}</p>
<Textarea placeholder="Type your comments here." className="flex w-full pb-12" onChange={getInputValue} />
<button onClick={() => postData()} className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-4 py-1">
{t("send")}
</button>
</div>
<div className="border-b-2 border-slate-300 mt-4 w-auto"></div>
<div>
{listSuggestion?.map((data: any) => (
<div className="flex flex-col">
<div className="flex flex-row mt-2 px-4 lg:px-14">
<img src={data?.suggestionFrom?.profilePictureUrl} className="h-12 lg:h-16 w-12 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{Number(data.suggestionFrom?.roleId) == 2 || Number(data.suggestionFrom?.roleId) == 3 || Number(data.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : data.suggestionFrom?.fullname}
{getPublicLocaleTimestamp(new Date(data.createdAt))}
</p>
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{data?.message}</p>
<div>
<a
style={
Number(data.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${data.id}`)}
className="mr-2"
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
{Number(data.suggestionFrom?.id) == Number(userId) || Number(userRoleId) == 2 ? (
<a onClick={() => deleteData(data.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
) : (
""
)}
</div>
</div>
</div>
{visibleInput === `comment-id-${data.id}` && (
<div id={`comment-id-${data.id}`} className="px-4 pl-[72px] lg:px-14 lg:pl-32 mt-2 ">
<Textarea id={`input-comment-${data.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
<div className="flex flex-row gap-3">
<a onClick={() => postDataChild(data.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 mt-2 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${data.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg mt-2 w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
{data.children.length > 0
? data.children?.map((child1: any) => (
<div className="flex flex-col">
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-16 lg:pl-32">
<img src={child1.suggestionFrom?.profilePictureUrl} onError={addDefaultProfile} alt="" className="h-10 lg:h-16 w-10 lg:w-16 mr-2" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{" "}
<b>{Number(child1.suggestionFrom?.roleId) == 2 || Number(child1.suggestionFrom?.roleId) == 3 || Number(child1.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child1.suggestionFrom?.fullname}</b>{" "}
{getPublicLocaleTimestamp(new Date(child1.createdAt))}
</p>
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{parse(String(child1?.message))}</p>
<div>
<a
style={
Number(child1.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${child1.id}`)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
<a
style={
Number(child1.suggestionFrom?.id) == Number(userId)
? {}
: {
display: "none",
}
}
onClick={() => deleteData(child1.id)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
</div>
</div>
</div>
{visibleInput === `comment-id-${child1.id}` && (
<div id={`comment-id-${child1.id}`} className="px-4 lg:px-14 pl-28 lg:pl-[200px]">
<Textarea name="" className="mt-2 " id={`input-comment-${child1.id}`} placeholder={t("enterReply")} />
<div className="flex flex-row mt-2 gap-3">
<a onClick={() => postDataChild(child1.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${child1.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
{child1.children.length > 0
? child1.children?.map((child2: any) => (
<div className="">
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-28 lg:pl-48">
<img src={child2.suggestionFrom?.profilePictureUrl} className="h-9 lg:h-16 w-9 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{" "}
<b>{Number(child2.suggestionFrom?.roleId) == 2 || Number(child2.suggestionFrom?.roleId) == 3 || Number(child2.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child2.suggestionFrom?.fullname}</b>{" "}
{getPublicLocaleTimestamp(new Date(child2.createdAt))}
</p>
<p className="text-slate-500 text-sm mb-4">{parse(String(child2?.message))}</p>
<div>
<a
style={
Number(child2.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${child2.id}`)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
<a
style={
Number(child2.suggestionFrom?.id) == Number(userId)
? {}
: {
display: "none",
}
}
onClick={() => deleteData(child2.id)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
</div>
</div>
</div>
{visibleInput === `comment-id-${child2.id}` && (
<div id={`comment-id-${child2.id}`} className="px-4 lg:px-14 pl-40 lg:pl-[265px]">
<Textarea name="" id={`input-comment-${child2.id}`} className="my-2" placeholder={t("enterReply")} />
<div className="flex flex-row gap-3">
<a onClick={() => postDataChild(child2.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${child2.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
</div>
))
: ""}
</div>
))
: ""}
</div>
))}
</div>
</div>
{/* Konten Serupa */}
<div className="">
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailDocument;
export async function generateMetadata({ params }: any): Promise<Metadata> {
const slug = String(params?.slug);
const res = await getDetailMetaData(String(slug));
const document = res?.data?.data;
// console.log("image", image);
return {
title: document.title,
description: document.description,
openGraph: {
title: document?.title,
description: document?.description,
images: [`${document?.thumbnailLink}`],
},
};
}
export default async function DetailInfo({ params }: Props) {
return <DetailDocument />;
}

File diff suppressed because it is too large Load Diff

View File

@ -10,13 +10,14 @@ import * as z from "zod";
import Swal from "sweetalert2";
import { getBlog, postBlog } from "@/service/blog/blog";
import { id } from "date-fns/locale";
import router from "next/router";
import { getInfoProfile, saveUser, setupPassword } from "@/service/auth";
import withReactContent from "sweetalert2-react-content";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { Link } from "@/components/navigation";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { close, error, loading } from "@/lib/swal";
const profileSchema = z.object({
password: z.string().min(1, { message: "Judul diperlukan" }),
@ -31,6 +32,7 @@ type Detail = {
};
const ChangePassword: React.FC = () => {
const router = useRouter();
const MySwal = withReactContent(Swal);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
@ -59,26 +61,52 @@ const ChangePassword: React.FC = () => {
}, []);
const save = async (data: ProfileSchema) => {
const requestData = {
...data,
// userId: detail?.userKeycloakId,
password: data.password,
retypePassword: detail?.retypePassword,
};
if (data.password != null && data.password == data.retypePassword) {
loading();
const newData = {
// userId: detail?.userKeycloakId,
password: data.password,
retypePassword: detail?.retypePassword,
};
const response = await setupPassword(requestData);
console.log("Form Data Submitted:", requestData);
console.log("response", response);
const response = await setupPassword(newData);
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/auth");
});
close();
if (response?.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/in/auth");
});
}
// const requestData = {
// ...data,
// // userId: detail?.userKeycloakId,
// password: data.password,
// retypePassword: detail?.retypePassword,
// };
// const response = await setupPassword(requestData);
// console.log("Form Data Submitted:", requestData);
// console.log("response", response);
// MySwal.fire({
// title: "Sukses",
// text: "Data berhasil disimpan.",
// icon: "success",
// confirmButtonColor: "#3085d6",
// confirmButtonText: "OK",
// }).then(() => {
// router.push("/in/auth");
// });
};
const onSubmit = (data: ProfileSchema) => {
@ -107,13 +135,19 @@ const ChangePassword: React.FC = () => {
<div className="flex justify-center gap-4 mb-8">
<Link href={"/profile"}>
<button className="border border-red-700 text-red-700 px-4 py-2 rounded">{t("userProfile")}</button>
<button className="border border-red-700 text-red-700 px-4 py-2 rounded">
{t("userProfile")}
</button>
</Link>
<Link href={"/profile/change-profile"}>
<button className="border border-red-700 text-red-700 px-4 py-2 rounded">{t("changePhoto")}</button>
<button className="border border-red-700 text-red-700 px-4 py-2 rounded">
{t("changePhoto")}
</button>
</Link>
<Link href={"/profile/change-password"}>
<button className="bg-red-700 text-white px-4 py-2 rounded">{t("changePass")}</button>
<button className="bg-red-700 text-white px-4 py-2 rounded">
{t("changePass")}
</button>
</Link>
</div>
@ -126,20 +160,55 @@ const ChangePassword: React.FC = () => {
{t("password")}
<span className="text-red-500">*</span>
</Label>
<Controller control={control} name="password" render={({ field }) => <Input size="md" type="password" defaultValue={field.value} onChange={field.onChange} placeholder={t("inputPass")} />} />
{errors.password?.message && <p className="text-red-400 text-sm">{errors.password.message}</p>}
<Controller
control={control}
name="password"
render={({ field }) => (
<Input
size="md"
type="password"
defaultValue={field.value}
onChange={field.onChange}
placeholder={t("inputPass")}
/>
)}
/>
{errors.password?.message && (
<p className="text-red-400 text-sm">
{errors.password.message}
</p>
)}
</div>
<div className="mb-4">
<Label>
{t("confirmPass")}
<span className="text-red-500">*</span>
</Label>
<Controller control={control} name="retypePassword" render={({ field }) => <Input size="md" type="password" defaultValue={field.value} onChange={field.onChange} placeholder={t("samePass")} />} />
{errors.retypePassword?.message && <p className="text-red-400 text-sm">{errors.retypePassword.message}</p>}
<Controller
control={control}
name="retypePassword"
render={({ field }) => (
<Input
size="md"
type="password"
defaultValue={field.value}
onChange={field.onChange}
placeholder={t("samePass")}
/>
)}
/>
{errors.retypePassword?.message && (
<p className="text-red-400 text-sm">
{errors.retypePassword.message}
</p>
)}
</div>
<div className="text-right">
<div className="mt-4">
<button type="submit" className="bg-red-700 text-white px-6 py-2 rounded hover:bg-red-800 focus:outline-none focus:ring focus:ring-red-300">
<button
type="submit"
className="bg-red-700 text-white px-6 py-2 rounded hover:bg-red-800 focus:outline-none focus:ring focus:ring-red-300"
>
{t("save")}
</button>
</div>

View File

@ -1,788 +1,48 @@
"use client";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import { checkWishlistStatus, createPublicSuggestion, deletePublicSuggestion, deleteWishlist, getDetail, getPublicSuggestionList, saveWishlist } from "@/service/landing/landing";
import VideoPlayer from "@/utils/video-player";
import NewContent from "@/components/landing-page/new-content";
import { Link, useRouter } from "@/i18n/routing";
import { Textarea } from "@/components/ui/textarea";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, error, loading, successCallback, warning } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { checkMaliciousText, formatDateToIndonesian, getPublicLocaleTimestamp } from "@/utils/globals";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import parse from "html-react-parser";
import { useTranslations } from "next-intl";
import Image from "next/image";
import { postActivityLog } from "@/service/content/content";
import {
checkWishlistStatus,
createPublicSuggestion,
deletePublicSuggestion,
deleteWishlist,
getDetail,
getDetailMetaData,
getPublicSuggestionList,
saveWishlist,
} from "@/service/landing/landing";
import { Metadata } from "next";
import DetailImage from "@/components/main/image-detail";
import DetailVideo from "@/components/main/video-detail";
interface Size {
label: string;
value: string;
}
const DetailVideo = () => {
const [selectedSize, setSelectedSize] = useState<string>("L");
const [selectedTab, setSelectedTab] = useState("video");
const router = useRouter();
const pathname = usePathname();
const params = useParams();
const slug = String(params?.slug);
const [detailDataVideo, setDetailDataVideo] = useState<any>();
const [isSaved, setIsSaved] = useState(false);
const [wishlistId, setWishlistId] = useState();
const { toast } = useToast();
const [isDownloadAll, setIsDownloadAll] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [isFromSPIT, setIsFromSPIT] = useState(false);
const [main, setMain] = useState<any>();
const [resolutionSelected, setResolutionSelected] = useState("720");
const userId = getCookiesDecrypt("uie");
const [emailShareList, setEmailShareList] = useState<any>();
const [emailShareInput, setEmailShareInput] = useState<any>();
const [emailMessageInput, setEmailMessageInput] = useState();
const searchParams = useSearchParams();
const id = searchParams?.get("id");
const [width, setWidth] = useState<any>();
const [content, setContent] = useState<any>([]);
const userRoleId = getCookiesDecrypt("urie");
const [message, setMessage] = useState("");
const [listSuggestion, setListSuggestion] = useState<any>();
const [visibleInput, setVisibleInput] = useState(null);
const MySwal = withReactContent(Swal);
const t = useTranslations("LandingPage");
let typeString = "video";
useEffect(() => {
initFetch();
checkWishlist();
sendActivityLog(2);
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailVideo", response);
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
setIsFromSPIT(response?.data?.data?.isFromSPIT);
setWidth(window.innerWidth);
setContent(response?.data.data);
setListSuggestion(responseGet.data?.data);
setMain({
id: response?.data?.data?.files[0]?.id,
type: response?.data?.data?.fileType.name,
url:
Number(response?.data?.data?.fileType?.id) == 4
? response?.data?.data?.files[0]?.secondaryUrl
: Number(response?.data?.data?.fileType?.id) == 2
? `${process.env.NEXT_PUBLIC_API}/media/view?id=${response?.data?.data?.files[0]?.id}&operation=file&type=video`
: response?.data?.data?.files[0]?.url,
thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
names: response?.data?.data?.files[0]?.fileName,
format: response?.data?.data?.files[0]?.format,
widthPixel: response?.data?.data?.files[0]?.widthPixel,
heightPixel: response?.data?.data?.files[0]?.heightPixel,
size: response?.data?.data?.files[0]?.size,
caption: response?.data?.data?.files[0]?.caption,
});
setDetailDataVideo(response?.data?.data);
type Props = {
params: {
title: string;
slug: string;
description: string;
thumbnailLink: string;
};
const doBookmark = async () => {
if (userId) {
const data = {
mediaUploadId: slug?.split("-")?.[0],
};
loading();
const res = await saveWishlist(data);
if (res?.error) {
error(res.message);
return false;
}
close();
toast({
title: "Konten berhasil disimpan",
});
checkWishlist();
} else {
router.push("/auth");
}
};
async function checkWishlist() {
if (userId) {
const res = await checkWishlistStatus(slug.split("-")?.[0]);
console.log(res?.data?.data);
const isAlreadyOnWishlist = res?.data?.data !== "-1";
setWishlistId(res?.data?.data);
setIsSaved(isAlreadyOnWishlist);
}
}
const handleDeleteWishlist = async () => {
if (userId) {
loading();
const res = await deleteWishlist(wishlistId);
if (res?.error) {
error(res.message);
return false;
}
toast({
title: "Konten berhasil dihapus",
});
close();
checkWishlist();
} else {
router.push("/auth");
}
};
const sizes = [
{ label: "FULL HD", value: "1920 x 1080 px" },
{ label: "HD", value: "1280 x 720 px" },
{ label: "SD", value: "720 x 480 px" },
{ label: "WEB", value: "640 x 360 px" },
];
async function sendActivityLog(activityTypeId: number) {
const data = {
activityTypeId,
mediaId: slug.split("-")?.[0],
url: window.location.href,
};
// set activity
await postActivityLog(data);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} else {
sendActivityLog(2);
sendActivityLog(3);
if (isDownloadAll) {
let url: string;
const baseId = slug.split("-")?.[0];
// if (type === "1") {
url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${resolutionSelected}`;
// } else if (type === "2") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${imageSizeSelected}`;
// } else {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}`;
// }
downloadFile(url, "FileDownload.zip");
} else {
if (isFromSPIT && main?.url?.includes("spit.humas")) {
downloadFile(`${main?.url}`, `${main.names}`);
} else {
const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
downloadFile(url, `${main.names}`);
}
}
// } else if (type === "1" && resolutionSelected?.length > 0) {
// if (isFromSPIT && main?.url?.includes("spit.humas")) {
// downloadFile(`${main?.url}`, `${main.names}`);
// } else {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
// downloadFile(url, `${main.names}`);
// }
// } else if (type === "2" && imageSizeSelected?.length > 0) {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=image&resolution=${imageSizeSelected}`;
// downloadFile(url, `${main.names}`);
// } else if (type === "3" || type === "4") {
// downloadFile(`${main?.url}`, `${main.names}`);
// }
}
}
};
const downloadFile = (fileUrl: string, name: string) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl, true);
xhr.responseType = "blob";
xhr.addEventListener("progress", (event) => {
if (event.lengthComputable) {
const percentCompleted = Math.round((event.loaded / event.total) * 100);
setDownloadProgress(percentCompleted);
}
});
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const contentType = xhr.getResponseHeader("content-type") || "application/octet-stream";
const extension = contentType.split("/")[1];
const filename = `${name}.${extension}`;
const blob = new Blob([xhr.response], {
type: contentType,
});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = filename;
document.body.append(a);
a.click();
a.remove();
}
});
xhr.onloadend = () => {
setDownloadProgress(0);
};
xhr.send();
};
const handleShare = (type: any, url: any) => {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth/login");
} else {
sendActivityLog(2);
sendActivityLog(4);
if (type == "wa" && width <= 768) {
window.open(`whatsapp://send?${url}`, "_blank");
} else if (type == "wa" && width > 768) {
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
} else {
window.open(url);
}
}
};
async function shareToEmail() {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth/login");
} else {
const data = {
mediaUploadId: id?.split("-")?.[0],
email: emailShareList || [emailShareInput],
message: emailMessageInput,
url: window.location.href,
};
loading();
const res = await sendMediaUploadToEmail(data);
if (res?.error) {
error(res.message);
return false;
}
close();
successCallback("Konten Telah Dikirim");
}
}
const handleEmailList = (e: any) => {
const arrayEmail: any = [];
for (let i = 0; i < emailShareList?.length; i += 1) {
arrayEmail.push(emailShareList[i]);
}
if (e.which == 13) {
if (e.target.value) {
arrayEmail.push(e.target.value);
setEmailShareList(arrayEmail);
setEmailShareInput("");
}
e.preventDefault();
}
return false;
};
async function sendSuggestionChild(parentId: any) {
const inputElement = document.querySelector(`#input-comment-${parentId}`) as HTMLInputElement;
if (inputElement && inputElement.value.length > 3) {
loading();
const data = {
mediaUploadId: slug?.split("-")?.[0],
message: inputElement.value,
parentId,
};
console.log(data);
const response = await createPublicSuggestion(data);
console.log(response);
const responseGet: any = await getPublicSuggestionList(slug?.split("-")?.[0]);
console.log(responseGet.data?.data);
setListSuggestion(responseGet.data?.data);
// Reset input field
inputElement.value = "";
// document.querySelector("#comment-id-" + parentId)?.style.display = "none";
close();
}
}
async function deleteDataSuggestion(dataId: any) {
loading();
const response = await deletePublicSuggestion(dataId);
console.log(response);
const responseGet = await getPublicSuggestionList(slug.split("-")?.[0]);
console.log(responseGet.data?.data);
setListSuggestion(responseGet.data?.data);
close();
}
const deleteData = (dataId: any) => {
MySwal.fire({
title: "Delete Comment",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Delete",
}).then((result: any) => {
if (result.isConfirmed) {
deleteDataSuggestion(dataId);
console.log(dataId);
}
});
};
const showInput = (e: any) => {
console.log(document.querySelector(`#${e}`)?.classList);
document.querySelector(`#${e}`)?.classList.toggle("none");
setVisibleInput(visibleInput === e ? null : e);
};
function addDefaultProfile(ev: any) {
ev.target.src = "/assets/avatar-profile.png";
}
async function sendSuggestionParent() {
if (message?.length > 3) {
loading();
const data = {
mediaUploadId: slug?.split("-")?.[0],
message,
parentId: null,
};
const response = await createPublicSuggestion(data);
console.log(response);
setMessage("");
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
console.log(responseGet?.data?.data);
setListSuggestion(responseGet?.data?.data);
// Hapus nilai semua input secara manual jika perlu
const inputs = document.querySelectorAll("input");
inputs.forEach((input) => {
input.value = "";
});
close();
}
}
const getInputValue = (e: any) => {
const message = e.target.value;
console.log(message);
setMessage(message);
};
const postData = () => {
const checkMessage = checkMaliciousText(message);
if (checkMessage == "") {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth");
} else {
sendSuggestionParent();
}
} else {
warning(checkMessage);
}
};
const postDataChild = (id: any) => {
const checkMessage = checkMaliciousText(message);
if (checkMessage == "") {
if (Number(userRoleId) < 1 || userRoleId == undefined) {
router.push("/auth");
} else {
sendSuggestionChild(id);
}
} else {
warning(checkMessage);
}
};
const shimmer = (w: number, h: number) => `
<svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g">
<stop stop-color="#bcbcbd" offset="20%" />
<stop stop-color="#f9fafb" offset="50%" />
<stop stop-color="#bcbcbd" offset="70%" />
</linearGradient>
</defs>
<rect width="${w}" height="${h}" fill="#bcbcbd" />
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
<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));
return (
<>
<div className="px-4 md:px-24 py-4">
{/* Container Utama */}
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
<div className="relative">
<VideoPlayer url={detailDataVideo?.files[0]?.url} />
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="text-gray-500 flex flex-col lg:flex-row justify-between items-center border-t mt-4">
{/* <p className="flex flex-row items-center mt-3">
oleh&nbsp;
<span className="font-semibold text-black">{detailDataVideo?.uploadedBy?.userLevel?.name}</span>
&nbsp;|&nbsp;Diupdate pada {detailDataVideo?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataVideo?.clickCount}
</p>
<p className="mt-3">Kreator: {detailDataVideo?.creatorName}</p> */}
<div className="flex flex-col lg:flex-row items-center mt-3 lg:justify-between">
<p className="text-xs lg:text-sm">
{t("by")}&nbsp;<span className="font-semibold text-black dark:text-white">{detailDataVideo?.uploadedBy?.userLevel?.name}</span>
</p>
{/* <p className="text-xs lg:text-sm">
&nbsp;|&nbsp;{t("updatedOn")}
{detailDataVideo?.updatedAt} WIB &nbsp;|&nbsp;
</p> */}
<p className="text-xs lg:text-sm">
&nbsp;|&nbsp;{t("updatedOn")}&nbsp;
{formatDateToIndonesian(new Date(detailDataVideo?.updatedAt))} {"WIB"}
</p>
<p className="text-xs lg:text-sm flex justify-center items-center">
&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;{detailDataVideo?.clickCount} &nbsp;
</p>
</div>
<div className="mt-3">
<p className="flex text-end text-xs lg:text-sm font-semibold">
{t("creator")}
{detailDataVideo?.creatorName}
</p>
</div>
</div>
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-lg lg:text-2xl my-8">{detailDataVideo?.title}</h1>
<div
className="font-light text-justify mb-5 space-y-4 lg:mb-0"
dangerouslySetInnerHTML={{
__html: detailDataVideo?.htmlDescription,
}}
/>
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] dark:bg-slate-600 rounded-lg mx-4 h-fit">
{isSaved ? (
<a onClick={() => handleDeleteWishlist()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark" width={40} />
<p className="text-base lg:text-lg">{t("delete")}</p>
</a>
) : (
<a onClick={() => doBookmark()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark-outline" width={40} />
<p className="text-base lg:text-lg">{t("save")}</p>
</a>
)}
{/* garis */}
<div className="border-t border-black my-4"></div>
<Link href={`/all/filter?title=polda&category=${detailDataVideo?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataVideo?.categoryName}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataVideo?.tags.split(",").map((tag: string) => (
<a onClick={() => router.push(`/all/filter?tag=${tag}`)} key={tag} className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500 hover:text-white">
{tag}
</a>
))}
</div>
<div className="border-t border-black my-4"></div>
{/* Opsi Ukuran Foto */}
<h4 className="flex text-lg justify-center items-center font-semibold my-3">{t("videoSize")}</h4>
<div className="border-t border-black my-4"></div>
<div className="space-y-2">
{sizes.map((size: any) => (
<div className="flex flex-row justify-between">
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
<div className="text-sm">{size.label}</div>
</div>
<div className="">
<div className="text-sm">{size.value}</div>
</div>
</div>
))}
</div>
{/* Download Semua */}
<div className="mt-4">
<label className="flex items-center space-x-2 text-sm">
<input type="checkbox" className="text-red-600 focus:ring-red-600" onChange={() => setIsDownloadAll(!isDownloadAll)} />
<span>{t("downloadAll")}</span>
</label>
</div>
{/* Tombol Download */}
<button onClick={handleDownload} className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="white" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
</svg>
{t("download")}
</button>
{/* Tombol Bagikan */}
<div className="flex flex-row justify-center py-3">
<p className="text-base font-semibold">{t("share")}</p>
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&quote=${content?.title}`)}>
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
</a>
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
</a>
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
</a>
<Popover>
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
</a>
</PopoverTrigger>
<PopoverContent>
<div className="flex flex-col">
<h1 className="mb-2">{t("shareTo")}</h1>
<div className="flex flex-col mb-2">
<p className="text-base font-semibold mb-1">{t("destinationEmail")}</p>
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder={t("pressEnter")} />
</div>
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
{t("send")}
</Button>
</div>
</PopoverContent>
</Popover>
</div>
</div>
</div>
</div>
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-4 lg:p-10 bg-[#f7f7f7] dark:bg-slate-600">
<div className="gap-5 flex flex-col px-4 lg:px-14">
<p className="flex items-start text-lg">{t("comment")}</p>
<Textarea placeholder="Type your comments here." className="flex w-full pb-12" onChange={getInputValue} />
<button onClick={() => postData()} className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-4 py-1">
{t("send")}
</button>
</div>
<div className="border-b-2 border-slate-300 mt-4 w-auto"></div>
<div>
{listSuggestion?.map((data: any) => (
<div className="flex flex-col">
<div className="flex flex-row mt-2 px-4 lg:px-14">
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1080} height={1080} src={data?.suggestionFrom?.profilePictureUrl} className="h-12 lg:h-16 w-12 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{Number(data.suggestionFrom?.roleId) == 2 || Number(data.suggestionFrom?.roleId) == 3 || Number(data.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : data.suggestionFrom?.fullname}
{getPublicLocaleTimestamp(new Date(data.createdAt))}
</p>
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{data?.message}</p>
<div>
<a
style={
Number(data.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${data.id}`)}
className="mr-2"
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
{Number(data.suggestionFrom?.id) == Number(userId) || Number(userRoleId) == 2 ? (
<a onClick={() => deleteData(data.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
) : (
""
)}
</div>
</div>
</div>
{visibleInput === `comment-id-${data.id}` && (
<div id={`comment-id-${data.id}`} className="px-4 pl-[72px] lg:px-14 lg:pl-32 mt-2 ">
<Textarea id={`input-comment-${data.id}`} className="p-4 focus:outline-none focus:border-sky-500" placeholder={t("enterReply")} />
<div className="flex flex-row gap-3">
<a onClick={() => postDataChild(data.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 mt-2 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${data.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg mt-2 w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
{data.children.length > 0
? data.children?.map((child1: any) => (
<div className="flex flex-col">
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-16 lg:pl-32">
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1080} height={1080} src={child1.suggestionFrom?.profilePictureUrl} onError={addDefaultProfile} alt="" className="h-10 lg:h-16 w-10 lg:w-16 mr-2" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{" "}
<b>{Number(child1.suggestionFrom?.roleId) == 2 || Number(child1.suggestionFrom?.roleId) == 3 || Number(child1.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child1.suggestionFrom?.fullname}</b>{" "}
{getPublicLocaleTimestamp(new Date(child1.createdAt))}
</p>
<p className="text-slate-500 text-[13px] lg:text-sm mb-4">{parse(String(child1?.message))}</p>
<div>
<a
style={
Number(child1.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${child1.id}`)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
<a
style={
Number(child1.suggestionFrom?.id) == Number(userId)
? {}
: {
display: "none",
}
}
onClick={() => deleteData(child1.id)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
</div>
</div>
</div>
{visibleInput === `comment-id-${child1.id}` && (
<div id={`comment-id-${child1.id}`} className="px-4 lg:px-14 pl-28 lg:pl-[200px]">
<Textarea name="" className="mt-2 " id={`input-comment-${child1.id}`} placeholder={t("enterReply")} />
<div className="flex flex-row mt-2 gap-3">
<a onClick={() => postDataChild(child1.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${child1.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
{child1.children.length > 0
? child1.children?.map((child2: any) => (
<div className="">
<div className="flex flex-row mt-2 px-4 lg:pr-14 pl-28 lg:pl-48">
<Image placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`} width={1080} height={1080} src={child2.suggestionFrom?.profilePictureUrl} className="h-9 lg:h-16 w-9 lg:w-16 mr-2" onError={addDefaultProfile} alt="" />
<div className="border border-slate-300 w-full p-2 lg:p-4 bg-white gap-1">
<p className="text-slate-500 text-sm lg:text-base border-b-2 border-slate-200 mb-2">
{" "}
<b>{Number(child2.suggestionFrom?.roleId) == 2 || Number(child2.suggestionFrom?.roleId) == 3 || Number(child2.suggestionFrom?.roleId) == 4 ? "HUMAS POLRI" : child2.suggestionFrom?.fullname}</b>{" "}
{getPublicLocaleTimestamp(new Date(child2.createdAt))}
</p>
<p className="text-slate-500 text-sm mb-4">{parse(String(child2?.message))}</p>
<div>
<a
style={
Number(child2.suggestionFrom?.id) == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => showInput(`comment-id-${child2.id}`)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("reply")}</small>
</a>
<a
style={
Number(child2.suggestionFrom?.id) == Number(userId)
? {}
: {
display: "none",
}
}
onClick={() => deleteData(child2.id)}
>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("delete")}</small>
</a>
</div>
</div>
</div>
{visibleInput === `comment-id-${child2.id}` && (
<div id={`comment-id-${child2.id}`} className="px-4 lg:px-14 pl-40 lg:pl-[265px]">
<Textarea name="" id={`input-comment-${child2.id}`} className="my-2" placeholder={t("enterReply")} />
<div className="flex flex-row gap-3">
<a onClick={() => postDataChild(child2.id)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("send")}</small>
</a>
<a onClick={() => showInput(`comment-id-${child2.id}`)}>
<small className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-2 text-xs lg:text-base lg:px-4 py-1 cursor-pointer">{t("cancel")}</small>
</a>
</div>
</div>
)}
</div>
))
: ""}
</div>
))
: ""}
</div>
))}
</div>
</div>
{/* Konten Serupa */}
<div className="">
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailVideo;
export async function generateMetadata({ params }: any): Promise<Metadata> {
const slug = String(params?.slug);
const res = await getDetailMetaData(String(slug));
const video = res?.data?.data;
// console.log("video", video);
return {
title: video.title,
description: video.description,
openGraph: {
title: video?.title,
description: video?.description,
videos: [`${video?.thumbnailLink}`],
},
};
}
export default async function DetailInfo({ params }: Props) {
return <DetailVideo />;
}

View File

@ -0,0 +1,256 @@
"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,
getUserLevelForExpert,
} 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 {
AdministrationLevelList,
getListCompetencies,
getListExperiences,
} from "@/service/management-user/management-user";
import { Link } from "@/i18n/routing";
const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
url: z.string().min(2, {
message: "Narasi Penugasan harus lebih dari 2 karakter.",
}),
assign: z.string().min(1, { message: "Judul diperlukan" }),
});
interface FileWithPreview extends File {
preview: string;
}
export type taskDetail = {
id: number;
title: string;
fileTypeOutput: string;
assignedToTopLevel: string;
url: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormMediaOnline() {
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);
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 [listExpert, setListExpert] = useState<any[]>([]);
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(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 {
register,
control,
setValue,
handleSubmit,
formState: { errors },
} = useForm<TaskSchema>({
resolver: zodResolver(taskSchema),
mode: "all",
});
// };
const save = async (data: TaskSchema) => {
const requestData: {
id?: number;
title: string;
assignedToUsers: any;
url: any;
} = {
...data,
assignedToUsers: data.assign,
url: data.url,
title: data.title,
};
const response = await createTaskTa(requestData);
console.log("Form Data Submitted:", requestData);
console.log("response", response);
const id = response?.data?.data.id;
};
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 successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
return (
<Card>
<div className="px-6 py-6">
<p className="text-xl mb-3">{t("data-media")}</p>
<p className="text-lg font-semibold mb-3">{t("form-media")}</p>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2">
<Label>{t("title-media-online")}</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={detail?.title}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p>
)}
</div>
<div className="mt-5 space-y-2">
<Label>{t("url")}</Label>
<Controller
control={control}
name="url"
render={({ field }) => (
<Input
size="md"
type="text"
value={detail?.url}
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 space-y-2">
<Label>{t("coverage-area")}</Label>
<Select onValueChange={setSelectedTarget}>
<SelectTrigger size="md">
<SelectValue placeholder="Choose" />
</SelectTrigger>
<SelectContent>
<SelectItem value="3,4">Semua Pengguna</SelectItem>
<SelectItem value="4">Kontributor</SelectItem>
<SelectItem value="3">Approver</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="flex flex-row items-center justify-end gap-3">
<div className="mt-4 ">
<Link href={"/admin/media-tracking/media-online"}>
<Button color="primary" variant={"outline"}>
{t("cancel")}
</Button>
</Link>
</div>
<div className="mt-4">
<Button type="submit" color="primary">
{t("submit")}
</Button>
</div>
</div>
</form>
</div>
</Card>
);
}

View File

@ -31,6 +31,7 @@ import {
getAcceptanceAssignmentStatus,
getAssignmentResponseList,
getMediaUpload,
getMediaUploadTa,
getTask,
getTaskTa,
getUserLevelForAssignments,
@ -407,7 +408,7 @@ export default function FormTaskTaDetail() {
const fetchAllData = async () => {
try {
const response = await getMediaUpload(id, userLevelId);
const response = await getMediaUploadTa(id, userLevelId);
setUploadResults(response?.data?.data || []);
} catch (error) {
console.error("Error fetching all data:", error);
@ -417,7 +418,7 @@ export default function FormTaskTaDetail() {
const fetchFilteredData = async (selectedLevels: any[]) => {
try {
if (selectedLevels.length === 0) {
fetchAllData(); // Jika tidak ada filter, panggil semua data
fetchAllData();
return;
}

View File

@ -25,6 +25,7 @@ import {
createTaskTa,
getTask,
getUserLevelForAssignments,
getUserLevelForExpert,
} from "@/service/task";
import {
Dialog,
@ -54,6 +55,11 @@ 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 {
AdministrationLevelList,
getListCompetencies,
getListExperiences,
} from "@/service/management-user/management-user";
const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -103,7 +109,6 @@ export default function FormTaskTa() {
const { id } = useParams() as { id: string };
console.log(id);
// State for various form fields
const [expertise, setExpertiseOutput] = useState({
semua: false,
komunikasi: false,
@ -119,8 +124,6 @@ export default function FormTaskTa() {
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>("");
@ -129,7 +132,14 @@ export default function FormTaskTa() {
const [detail, setDetail] = useState<taskDetail>();
const [refresh] = useState(false);
const [listDest, setListDest] = useState([]);
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [userExperiences, setUserExperiences] = useState<any>();
const [userLevels, setUserLevels] = useState<any>();
const [userCompetencies, setUserCompetencies] = useState<any[]>([]);
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
new Set()
);
const [listExpert, setListExpert] = useState<any[]>([]);
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [isLoading, setIsLoading] = useState(false);
const [audioFile, setAudioFile] = useState<File | null>(null);
@ -171,37 +181,56 @@ export default function FormTaskTa() {
mode: "all",
});
// const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// const selectedValue = Number(event.target.value);
// setMainType(selectedValue);
useEffect(() => {
getDataAdditional();
}, []);
// setPlatformTypeVisible(selectedValue === 2);
async function getDataAdditional() {
const resCompetencies = await getListCompetencies();
console.log("competency", resCompetencies);
setUserCompetencies(resCompetencies?.data?.data);
}
useEffect(() => {
async function fetchPoldaPolres() {
async function fetchListExpert() {
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);
const response = await getUserLevelForExpert(id);
setListExpert(response?.data?.data);
console.log("tenaga ahli", response?.data?.data);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
fetchListExpert();
}, []);
useEffect(() => {
const fetchExpertsForCompetencies = async () => {
const allExperts: any[] = [];
for (const compId of Array.from(selectedCompetencies)) {
const response = await getUserLevelForExpert(compId);
const experts = response?.data?.data || [];
allExperts.push(...experts);
}
const uniqueExperts = Array.from(
new Map(allExperts.map((e) => [e.id, e])).values()
);
setListExpert(uniqueExperts);
};
if (selectedCompetencies.size > 0) {
fetchExpertsForCompetencies();
} else {
setListExpert([]);
}
}, [selectedCompetencies]);
// };
const handleCheckboxChange = (levelId: number) => {
setCheckedLevels((prev) => {
@ -215,151 +244,39 @@ export default function FormTaskTa() {
});
};
const handlePoldaPolresChange = () => {
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
const handleExpertChange = () => {
return Array.from(checkedLevels).join(",");
};
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
) => {
if (key === "semua") {
const newState = {
semua: value,
mabes: value,
polda: value,
polres: value,
satker: value,
};
setUnitSelection(newState);
} else {
const updatedSelection = {
...unitSelection,
[key]: value,
};
const allChecked = ["mabes", "polda", "polres", "satker"].every(
(k) => updatedSelection[k as keyof typeof unitSelection]
);
updatedSelection.semua = allChecked;
setUnitSelection(updatedSelection);
}
};
const handleExpertiseOutputChange = (
key: keyof typeof expertise,
value: boolean
) => {
if (key === "semua") {
const newState = {
semua: value,
komunikasi: value,
hukum: value,
bahasa: value,
ekonomi: value,
politik: value,
sosiologi: value,
ilmuadministrasipemerintah: value,
ti: value,
};
setExpertiseOutput(newState);
} else {
const updated = {
...expertise,
[key]: value,
};
const allChecked = ["video", "audio", "image", "text"].every(
(k) => updated[k as keyof typeof expertise]
);
updated.semua = allChecked;
setExpertiseOutput(updated);
}
};
const handleExpertOutputChange = (
key: keyof typeof expert,
value: boolean
) => {
if (key === "semua") {
const newState = {
semua: value,
};
setExpertOutput(newState);
} else {
const updated = {
...expert,
[key]: value,
};
const allChecked = ["video", "audio", "image", "text"].every(
(k) => updated[k as keyof typeof expert]
);
updated.semua = allChecked;
setExpertOutput(updated);
}
const handleCompetencyChange = async (competencyId: number) => {
setSelectedCompetencies((prev) => {
const updated = new Set(prev);
if (updated.has(competencyId)) {
updated.delete(competencyId);
} else {
updated.add(competencyId);
}
return updated;
});
};
const save = async (data: TaskSchema) => {
const fileTypeMapping = {
all: "1",
video: "2",
audio: "4",
image: "3",
text: "5",
};
const unitMapping = {
allUnit: "0",
mabes: "1",
polda: "2",
polres: "3",
satker: "4",
};
const assignmentPurposeString = Object.keys(unitSelection)
.filter((key) => unitSelection[key as keyof typeof unitSelection])
.map((key) => unitMapping[key as keyof typeof unitMapping])
.join(",");
const selectedOutputs = Object.keys(expertise)
.filter((key) => expertise[key as keyof typeof expertise])
.map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping])
.join(",");
const requestData: {
id?: number;
title: string;
assignedToLevel: any;
assignedToUsers: any;
assignmentTypeId: string;
fileTypeOutput: string;
narration: string;
platformType: string | null;
assignmentMainTypeId: any;
assignmentType: string;
assignedToRole: string;
broadcastType: string;
expertCompetencies: string;
attachmentUrl: string[];
} = {
...data,
// assignmentType,
// assignmentCategory,
assignedToLevel: handlePoldaPolresChange(),
assignedToUsers: assignmentPurposeString,
assignedToRole: selectedTarget,
assignedToUsers: handleExpertChange(),
assignmentType: taskType,
broadcastType: broadcastType,
assignmentMainTypeId: mainType,
assignmentTypeId: type,
fileTypeOutput: selectedOutputs,
narration: data.naration,
platformType: "",
expertCompetencies: "1,2,3",
expertCompetencies: Array.from(selectedCompetencies).join(","),
title: data.title,
attachmentUrl: links,
};
@ -589,7 +506,6 @@ export default function FormTaskTa() {
setLinks([...links, ""]);
};
// Remove a specific link row
const handleRemoveRow = (index: number) => {
const updatedLinks = links.filter((_: any, i: any) => i !== index);
setLinks(updatedLinks);
@ -622,121 +538,7 @@ export default function FormTaskTa() {
<p className="text-red-400 text-sm">{errors.title.message}</p>
)}
</div>
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
<div className="mt-5 space-y-2">
<Label>{t("assignment-selection")}</Label>
<Select onValueChange={setSelectedTarget}>
<SelectTrigger size="md">
<SelectValue placeholder="Choose" />
</SelectTrigger>
<SelectContent>
<SelectItem value="3,4">Semua Pengguna</SelectItem>
<SelectItem value="4">Kontributor</SelectItem>
<SelectItem value="3">Approver</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex flex-wrap gap-3 mt-5 lg:pt-7 lg:ml-3 ">
{Object.keys(unitSelection).map((key) => (
<div className="flex items-center gap-2" key={key}>
<Checkbox
id={key}
checked={unitSelection[key as keyof typeof unitSelection]}
onCheckedChange={(value) =>
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
)
}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
))}
</div>
<div className="mt-6 lg:pt-6 lg:pl-3">
<Dialog>
<DialogTrigger asChild>
<Button variant="soft" size="sm" color="primary">
[{t("custom")}]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>Daftar Wilayah Polda dan Polres</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => (
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChange(polda.id)
}
className="mr-3"
/>
{polda.name}
<button
onClick={() => toggleExpand(polda.id)}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(polres.id)
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(polres.id);
} else {
updatedLevels.delete(polres.id);
}
}
);
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
Pilih Semua Polres
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChange(polres.id)
}
className="mr-2"
/>
{polres.name}
</Label>
))}
</div>
)}
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
</div>
<div className="mt-5 space-y-2">
<Label>{t("assigment-type")} </Label>
<RadioGroup
@ -793,21 +595,14 @@ export default function FormTaskTa() {
<div className="mt-5 space-y-2">
<Label>{t("areas-expertise")}</Label>
<div className="flex flex-wrap gap-4">
{Object.keys(expertise).map((key) => (
<div className="flex items-center gap-2" key={key}>
{userCompetencies?.map((item: any) => (
<div className="flex items-center gap-2" key={item.id}>
<Checkbox
id={key}
checked={expertise[key as keyof typeof expertise]}
onCheckedChange={(value) =>
handleExpertiseOutputChange(
key as keyof typeof expertise,
value as boolean
)
}
id={`comp-${item.id}`}
checked={selectedCompetencies.has(item.id)}
onCheckedChange={() => handleCompetencyChange(item.id)}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
</div>
))}
</div>
@ -815,23 +610,34 @@ export default function FormTaskTa() {
<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>
))}
<Dialog>
<DialogTrigger asChild>
<Button variant="soft" size="sm" color="primary">
[{"Pilih Tenaga Ahli"}]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listExpert?.map((expert: any) => (
<div key={expert.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
checked={checkedLevels.has(expert.id)}
onCheckedChange={() =>
handleCheckboxChange(expert.id)
}
className="mr-3"
/>
{expert.fullname}
</Label>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
</div>
<div className="mt-5 space-y-2">
@ -927,7 +733,6 @@ export default function FormTaskTa() {
</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) => (
@ -965,7 +770,6 @@ export default function FormTaskTa() {
</div>
</div>
{/* Submit Button */}
<div className="mt-4">
<Button type="submit" color="primary">
{t("submit")}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
"use client";
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
@ -9,7 +9,7 @@ import { Icon } from "@/components/ui/icon";
import { useForm, SubmitHandler } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { cn, setCookiesEncrypt } from "@/lib/utils";
import { cn, getCookiesDecrypt, setCookiesEncrypt } from "@/lib/utils";
import { Eye, EyeOff, Loader2 } from "lucide-react";
import {
getProfile,
@ -32,6 +32,13 @@ import {
} from "@/components/ui/input-otp";
import { error, loading } from "@/config/swal";
import { data } from "jquery";
import {
Dialog,
DialogContent,
DialogFooter,
DialogTrigger,
} from "@/components/ui/dialog";
import { getUserNotifications, listRole } from "@/service/landing/landing";
// Schema validasi menggunakan zod
const schema = z.object({
@ -58,11 +65,16 @@ const LoginForm = () => {
const [userIdentity] = useState();
const [email, setEmail] = useState();
const [category, setCategory] = useState("5");
const roleId = getCookiesDecrypt("urie");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [oldEmail, setOldEmail] = useState("");
const [oldEmailValidate, setOldEmailValidate] = useState("");
const [role, setRole] = useState<any>();
const [menuActive, setMenuActive] = useState<string>();
const [notifications, setNotifications] = useState([]);
const [notificationsUpdate, setNotificationsUpdate] = useState([]);
const [newEmail, setNewEmail] = useState("");
const [newEmailValidate, setNewEmailValidate] = useState("");
const [otpValidate, setOtpValidate] = useState("");
@ -341,6 +353,36 @@ const LoginForm = () => {
}
};
let menu = "";
useEffect(() => {
async function initState() {
setMenuActive(menu);
const res = await listRole();
setRole(res?.data?.data);
}
async function getNotif() {
if (roleId != undefined) {
const response = await getUserNotifications(0, 2);
setNotifications(response?.data?.data?.content);
console.log("respon:", response);
}
}
async function getNotifUpdate() {
if (roleId != undefined) {
const response = await getUserNotifications(0, 3);
setNotificationsUpdate(response?.data?.data?.content);
console.log("Notiffff:", response);
}
}
initState();
getNotif();
getNotifUpdate();
}, []);
return (
<form onSubmit={handleSubmit(onSubmit)} className="mt-5 2xl:mt-7 space-y-4">
{step === 1 ? (
@ -350,7 +392,50 @@ const LoginForm = () => {
{t("logInPlease")}
</h4>
<div className="text-default-500 text-base">
{t("acc")} <span className="text-red-500">{t("reg")}</span>
{t("acc")}
<Dialog>
<DialogTrigger asChild>
<span className="w-full lg:w-fit px-2 h-8 text-red-500 hover:cursor-pointer">
{t("register")}
</span>
</DialogTrigger>
<DialogContent size="sm" className="sm:max-w-[425px]">
<div className="flex flex-col w-full gap-1">
<p className="text-lg font-semibold text-center">
{t("categoryReg")}
</p>
<p className="text-base text-center">{t("selectOne")}</p>
</div>
<div>
{role?.map((row: any) => (
<div key={row.id}>
<input
type="radio"
id={`category${row.id}`}
name="category"
className=""
value={row.id}
checked={category == `${row.id}`}
onChange={(event) => setCategory(event.target.value)}
/>
<label className="ml-2" htmlFor={`category${row.id}`}>
{row.name}
</label>
</div>
))}
</div>
<div className="border-b-2 border-black"></div>
<DialogFooter>
<Link
href={`/auth/registration?category=${category}`}
className="flex justify-center bg-red-500 px-4 py-1 rounded-md border border-black text-white"
type="submit"
>
{t("next")}{" "}
</Link>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
<div className="space-y-2">

View File

@ -26,7 +26,7 @@ export default function PerformancePolresViz() {
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-polda_rev100/db-ranking-13-polda?"
: `/views/2023_09_db-ranking-polres-by-polda_rev100/db-ranking-by-polda?polda-selected=${poldaState}&`;
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";

View File

@ -2763,20 +2763,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "uiw:user-delete",
children: [],
},
{
href: "/supervisor/communications/ptt",
label: t("ptt"),
active: pathname.includes("/communications/ptt"),
icon: "clarity:employee-group-line",
children: [],
},
{
href: "/supervisor/communications/web-chat",
label: t("web-chat"),
active: pathname.includes("/communications/web-chat"),
icon: "clarity:employee-group-line",
children: [],
},
// {
// href: "/supervisor/communications/ptt",
// label: t("ptt"),
// active: pathname.includes("/communications/ptt"),
// icon: "clarity:employee-group-line",
// children: [],
// },
// {
// href: "/supervisor/communications/web-chat",
// label: t("web-chat"),
// active: pathname.includes("/communications/web-chat"),
// icon: "clarity:employee-group-line",
// children: [],
// },
],
},
],

View File

@ -762,6 +762,11 @@
"sent": "Sent",
"accepted": "Accepted",
"assignment-status-details": "Assignment Status Details",
"unique-code": "Unique Code"
"unique-code": "Unique Code",
"form-media": "Form Add Media",
"data-media": "please complete the data! ",
"title-media-online": "Online Media Name",
"url": "Url",
"coverage-area": "Coverage Area"
}
}

View File

@ -762,6 +762,11 @@
"sent": "Terkirim",
"accepted": "Diterima",
"assignment-status-details": "Detail Status Penugasan",
"unique-code": "Kode Unik"
"unique-code": "Kode Unik",
"form-media": "Form Tambah Media",
"data-media": "Silahkan Lengkapi Data!",
"title-media-online": "Nama Media Online",
"url": "Url",
"coverage-area": "Cakupan Wilayah"
}
}

View File

@ -5,8 +5,32 @@ import { getCsrfToken } from "../auth";
import axiosBaseInstance from "./axios-base-instance";
import axiosInstanceJson from "./axiosInstanceJson";
export async function httpGetInterceptorForMetadata(pathUrl: any) {
const response = await axiosInterceptorInstance
.get(pathUrl)
.catch((error) => error.response);
console.log("Response interceptor : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else if (response?.status == 401) {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
export async function httpGetInterceptor(pathUrl: any) {
const pathname = window.location.pathname;
const pathname = window?.location.pathname;
const response = await axiosInterceptorInstance
.get(pathUrl)
.catch((error) => error.response);
@ -177,7 +201,7 @@ export async function httpGetInterceptorWithToken(pathUrl: any, headers?: any) {
export async function httpGetArrayBuffer(pathUrl: any, headers: any) {
const response = await axiosInterceptorInstance
.get(pathUrl, { headers, responseType: 'arraybuffer' })
.get(pathUrl, { headers, responseType: "arraybuffer" })
.catch((error) => error.response);
console.log("Response base svc : ", response);
if (response?.status == "200") {

View File

@ -1,5 +1,10 @@
import { httpGet, httpPost } from "../http-config/http-base-service";
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
import {
httpDeleteInterceptor,
httpGetInterceptor,
httpGetInterceptorForMetadata,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
export async function getCsrfToken() {
const pathUrl = "csrf";
@ -27,15 +32,25 @@ export async function getCsrfToken() {
}
export async function getHeroData() {
return await httpGetInterceptor(`media/public/list?enablePage=1&sort=desc&sortBy=createdAt&size=5&page=0&typeId=1&title=&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=`);
return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&sortBy=createdAt&size=5&page=0&typeId=1&title=&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=`
);
}
export async function getPublicCategoryData(group: any = "", type: string = "", isInt: Boolean = false) {
return await httpGetInterceptor(`media/categories/list/publish?enablePage=0&group=${group}&type=${type}&isInt=${isInt}`);
export async function getPublicCategoryData(
group: any = "",
type: string = "",
isInt: Boolean = false
) {
return await httpGetInterceptor(
`media/categories/list/publish?enablePage=0&group=${group}&type=${type}&isInt=${isInt}`
);
}
export async function getCategoryData() {
return await httpGetInterceptor(`media/categories/list/publish?enablePage=1&sort=desc&sortBy=updatedAt&size=12&type=`);
return await httpGetInterceptor(
`media/categories/list/publish?enablePage=1&sort=desc&sortBy=updatedAt&size=12&type=`
);
}
export async function getListContent(props: any) {
@ -49,15 +64,25 @@ export async function getPrivacy() {
}
export async function getIndeksData() {
return await httpGetInterceptor(`blog/public/pagination?enablePage=1&page=0&size=20`);
return await httpGetInterceptor(
`blog/public/pagination?enablePage=1&page=0&size=20`
);
}
export async function getDetail(slug: string) {
return await httpGetInterceptor(`media/public?slug=${slug}&state=mabes`);
}
export async function getDetailMetaData(slug: string) {
return await httpGetInterceptorForMetadata(
`media/public?slug=${slug}&state=mabes`
);
}
export async function getDetailIndeks() {
return await httpGetInterceptor(`blog/public/pagination?enablePage=1&page=0&size=6`);
return await httpGetInterceptor(
`blog/public/pagination?enablePage=1&page=0&size=6`
);
}
export async function getFeedback() {
@ -69,20 +94,50 @@ export async function postUserFeedback() {
}
export async function listCategory(type = "") {
return await httpGetInterceptor(`media/categories/list/enable?enablePage=1&sort=desc&sortBy=updatedAt&size=12&type=${type}`);
return await httpGetInterceptor(
`media/categories/list/enable?enablePage=1&sort=desc&sortBy=updatedAt&size=12&type=${type}`
);
}
export async function publicDetailBlog(slug: any) {
return await httpGetInterceptor(`blog/public/read/${slug}`);
}
export async function listData(type: string, search: string, category: string, size = 10, page = 0, sortBy = "createdAt", format = "", tag = "", group = "", startDate = "", endDate = "", month = "", year = "", isInt = false) {
export async function listData(
type: string,
search: string,
category: string,
size = 10,
page = 0,
sortBy = "createdAt",
format = "",
tag = "",
group = "",
startDate = "",
endDate = "",
month = "",
year = "",
isInt = false
) {
return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&sortBy=${sortBy}&size=${size}&page=${page}&typeId=${type}&title=${search}&categoryId=${category}&fileFormats=${format}&tags=${tag}&group=${group}&startDate=${startDate}&endDate=${endDate}&month=${month}&year=${year}&isInt=${isInt}`
);
}
export async function listDataRegional(type: string, search: string, category: string, format = "", tag = "", startDate = "", endDate = "", month = "", year = "", size = 10, page = 0, sortBy = "createdAt") {
export async function listDataRegional(
type: string,
search: string,
category: string,
format = "",
tag = "",
startDate = "",
endDate = "",
month = "",
year = "",
size = 10,
page = 0,
sortBy = "createdAt"
) {
return await httpGetInterceptor(
`media/public/regional-list?enablePage=1&size=${size}&page=${page}&sort=desc&sortBy=${sortBy}&typeId=${type}&title=${search}&categoryId=${category}&fileFormats=${format}&tags=${tag}&startDate=${startDate}&endDate=${endDate}&month=${month}&year=${year}`
);
@ -103,8 +158,19 @@ export async function getListPorvinces() {
export async function getUsersTeams(id: any) {
return await httpGetInterceptor(`users?instituteId=${id}`);
}
export async function mediaWishlist(type: any, instituteId: any, search: any, category: any, size: string, page: number, sortBy: undefined, format: string) {
return await httpGetInterceptor(`/media/wishlist/list?enablePage=1&size=${size}&page=${page}&typeId=${type}&instituteId=${instituteId}`);
export async function mediaWishlist(
type: any,
instituteId: any,
search: any,
category: any,
size: string,
page: number,
sortBy: undefined,
format: string
) {
return await httpGetInterceptor(
`/media/wishlist/list?enablePage=1&size=${size}&page=${page}&typeId=${type}&instituteId=${instituteId}`
);
}
export async function checkWishlistStatus(mediaId: any) {
@ -121,14 +187,27 @@ export async function deleteWishlist(id: any) {
}
export async function getContentRewritePagination(page = 0, size = 10) {
return await httpGetInterceptor(`media/rewrite/pagination?size=${size}&page=${page}`);
return await httpGetInterceptor(
`media/rewrite/pagination?size=${size}&page=${page}`
);
}
export async function getContentRewrite(id: any) {
return await httpGetInterceptor(`media/rewrite?id=${id}`);
}
export async function listDataAll(type: any, search: any, category: any, format = "", tag = "", group = "", startDate = "", endDate = "", month = "", year = "") {
export async function listDataAll(
type: any,
search: any,
category: any,
format = "",
tag = "",
group = "",
startDate = "",
endDate = "",
month = "",
year = ""
) {
return await httpGetInterceptor(
`media/public/list?enablePage=1&size=20&sort=desc&typeId=${type}&title=${search}&categoryId=${category}&fileFormats=${format}&tags=${tag}&group=${group}&startDate=${startDate}&endDate=${endDate}&month=${month}&year=${year}`
);

View File

@ -53,6 +53,11 @@ export async function getMediaUpload(id: any, userLevelId: any) {
return httpGetInterceptor(url);
}
export async function getMediaUploadTa(id: any, userLevelId: any) {
const url = `/assignment-expert/media-uploads?id=${id}&userLevelId=${userLevelId}`;
return httpGetInterceptor(url);
}
export async function forwardTask(data: any) {
const url = "assignment/forward";
return httpPostInterceptor(url, data);
@ -63,11 +68,21 @@ export async function deleteTask(id: any) {
return httpDeleteInterceptor(url);
}
export async function deleteTaskTa(id: any) {
const url = `assignment-expert?id=${id}`;
return httpDeleteInterceptor(url);
}
export async function getUserLevelForAssignments() {
const url = "/users/user-levels/assignment";
return httpGetInterceptor(url);
}
export async function getUserLevelForExpert(id: any) {
const url = `/users/assignment-expert?competencyIds=${id}`;
return httpGetInterceptor(url);
}
export async function getAssignmentResponseList(id: any) {
const url = `assignment/response?assignmentId=${id}`;
return httpGetInterceptor(url);