feat:pull main
This commit is contained in:
commit
a241c28a95
|
|
@ -154,8 +154,6 @@ const AdvertisementsList = () => {
|
|||
page - 1,
|
||||
showData,
|
||||
"",
|
||||
categoryFilter?.sort().join(","),
|
||||
statusFilter?.sort().join(",")
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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 { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -22,6 +22,8 @@ import { useTranslations } from "next-intl";
|
|||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const MySwal = withReactContent(Swal);
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
|
|
@ -121,13 +123,24 @@ const useTableColumns = () => {
|
|||
"menunggu review": "bg-orange-100 text-orange-600",
|
||||
};
|
||||
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const colors = [
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-green-100 text-green-600",
|
||||
"bg-blue-100 text-blue-600",
|
||||
"bg-red-200 text-red-600",
|
||||
];
|
||||
|
||||
const status =
|
||||
isPublish || isPublishOnPolda ? "diterima" : "menunggu review";
|
||||
|
||||
const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
Number(row.original?.statusId) == 2 &&
|
||||
row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) &&
|
||||
Number(row.original?.creatorGroupLevelId) != Number(userLevelId)
|
||||
? "1"
|
||||
: row.original?.statusId;
|
||||
const statusStyles =
|
||||
colors[Number(status)] || "bg-red-200 text-red-600";
|
||||
// const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
|
||||
return (
|
||||
<Badge
|
||||
|
|
@ -136,7 +149,18 @@ const useTableColumns = () => {
|
|||
statusStyles
|
||||
)}
|
||||
>
|
||||
{status}
|
||||
{(Number(row.original?.statusId) == 2 &&
|
||||
!row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(
|
||||
`:${Number(userLevelId)}:`
|
||||
) &&
|
||||
Number(row.original?.creatorGroupLevelId) !=
|
||||
Number(userLevelId)) ||
|
||||
(Number(row.original?.statusId) == 1 &&
|
||||
Number(row.original?.needApprovalFromLevel) ==
|
||||
Number(userLevelId))
|
||||
? "Menunggu Review"
|
||||
: row.original?.statusName}{" "}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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 { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -25,6 +25,8 @@ import { useTranslations } from "next-intl";
|
|||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const MySwal = withReactContent(Swal);
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
|
|
@ -125,13 +127,24 @@ const useTableColumns = () => {
|
|||
"menunggu review": "bg-orange-100 text-orange-600",
|
||||
};
|
||||
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const colors = [
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-green-100 text-green-600",
|
||||
"bg-blue-100 text-blue-600",
|
||||
"bg-red-200 text-red-600",
|
||||
];
|
||||
|
||||
const status =
|
||||
isPublish || isPublishOnPolda ? "diterima" : "menunggu review";
|
||||
|
||||
const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
Number(row.original?.statusId) == 2 &&
|
||||
row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) &&
|
||||
Number(row.original?.creatorGroupLevelId) != Number(userLevelId)
|
||||
? "1"
|
||||
: row.original?.statusId;
|
||||
const statusStyles =
|
||||
colors[Number(status)] || "bg-red-200 text-red-600";
|
||||
// const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
|
||||
return (
|
||||
<Badge
|
||||
|
|
@ -140,7 +153,18 @@ const useTableColumns = () => {
|
|||
statusStyles
|
||||
)}
|
||||
>
|
||||
{status}
|
||||
{(Number(row.original?.statusId) == 2 &&
|
||||
!row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(
|
||||
`:${Number(userLevelId)}:`
|
||||
) &&
|
||||
Number(row.original?.creatorGroupLevelId) !=
|
||||
Number(userLevelId)) ||
|
||||
(Number(row.original?.statusId) == 1 &&
|
||||
Number(row.original?.needApprovalFromLevel) ==
|
||||
Number(userLevelId))
|
||||
? "Menunggu Review"
|
||||
: row.original?.statusName}{" "}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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 { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -22,6 +22,8 @@ import { useTranslations } from "next-intl";
|
|||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const MySwal = withReactContent(Swal);
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
|
|
@ -122,13 +124,24 @@ const useTableColumns = () => {
|
|||
"menunggu review": "bg-orange-100 text-orange-600",
|
||||
};
|
||||
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const colors = [
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-green-100 text-green-600",
|
||||
"bg-blue-100 text-blue-600",
|
||||
"bg-red-200 text-red-600",
|
||||
];
|
||||
|
||||
const status =
|
||||
isPublish || isPublishOnPolda ? "diterima" : "menunggu review";
|
||||
|
||||
const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
Number(row.original?.statusId) == 2 &&
|
||||
row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) &&
|
||||
Number(row.original?.creatorGroupLevelId) != Number(userLevelId)
|
||||
? "1"
|
||||
: row.original?.statusId;
|
||||
const statusStyles =
|
||||
colors[Number(status)] || "bg-red-200 text-red-600";
|
||||
// const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
|
||||
return (
|
||||
<Badge
|
||||
|
|
@ -137,7 +150,18 @@ const useTableColumns = () => {
|
|||
statusStyles
|
||||
)}
|
||||
>
|
||||
{status}
|
||||
{(Number(row.original?.statusId) == 2 &&
|
||||
!row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(
|
||||
`:${Number(userLevelId)}:`
|
||||
) &&
|
||||
Number(row.original?.creatorGroupLevelId) !=
|
||||
Number(userLevelId)) ||
|
||||
(Number(row.original?.statusId) == 1 &&
|
||||
Number(row.original?.needApprovalFromLevel) ==
|
||||
Number(userLevelId))
|
||||
? "Menunggu Review"
|
||||
: row.original?.statusName}{" "}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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 { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -22,6 +22,7 @@ import { useTranslations } from "next-intl";
|
|||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const MySwal = withReactContent(Swal);
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -123,13 +124,24 @@ const useTableColumns = () => {
|
|||
"menunggu review": "bg-orange-100 text-orange-600",
|
||||
};
|
||||
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const colors = [
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-orange-100 text-orange-600",
|
||||
"bg-green-100 text-green-600",
|
||||
"bg-blue-100 text-blue-600",
|
||||
"bg-red-200 text-red-600",
|
||||
];
|
||||
|
||||
const status =
|
||||
isPublish || isPublishOnPolda ? "diterima" : "menunggu review";
|
||||
|
||||
const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
Number(row.original?.statusId) == 2 &&
|
||||
row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) &&
|
||||
Number(row.original?.creatorGroupLevelId) != Number(userLevelId)
|
||||
? "1"
|
||||
: row.original?.statusId;
|
||||
const statusStyles =
|
||||
colors[Number(status)] || "bg-red-200 text-red-600";
|
||||
// const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||
|
||||
return (
|
||||
<Badge
|
||||
|
|
@ -138,7 +150,18 @@ const useTableColumns = () => {
|
|||
statusStyles
|
||||
)}
|
||||
>
|
||||
{status}
|
||||
{(Number(row.original?.statusId) == 2 &&
|
||||
!row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(
|
||||
`:${Number(userLevelId)}:`
|
||||
) &&
|
||||
Number(row.original?.creatorGroupLevelId) !=
|
||||
Number(userLevelId)) ||
|
||||
(Number(row.original?.statusId) == 1 &&
|
||||
Number(row.original?.needApprovalFromLevel) ==
|
||||
Number(userLevelId))
|
||||
? "Menunggu Review"
|
||||
: row.original?.statusName}{" "}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import { useRouter, useSearchParams } from "next/navigation";
|
|||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./columns";
|
||||
import {
|
||||
paginationCalendar,
|
||||
getCalendarPagination,
|
||||
paginationSchedule,
|
||||
} from "@/service/schedule/schedule";
|
||||
import { CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
|
@ -120,10 +120,9 @@ const CalendarPolriTable = () => {
|
|||
|
||||
async function fetchData() {
|
||||
try {
|
||||
const res = await paginationCalendar(
|
||||
const res = await getCalendarPagination(
|
||||
showData,
|
||||
page - 1,
|
||||
1,
|
||||
search,
|
||||
statusFilter
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,12 @@ import { Button } from "@/components/ui/button";
|
|||
import { Badge } from "@/components/ui/badge";
|
||||
import { Link } from "@/components/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import { deleteSchedule } from "@/service/schedule/schedule";
|
||||
import { error } from "@/config/swal";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const useTableColumns = (props: { selectedTypeSchedule: string }) => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
|
|
@ -138,6 +142,33 @@ const useTableColumns = () => {
|
|||
header: t("action"),
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
async function doDelete(id: any) {
|
||||
// loading();
|
||||
|
||||
const response = await deleteSchedule(id);
|
||||
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
success();
|
||||
}
|
||||
|
||||
function success() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -151,7 +182,7 @@ const useTableColumns = () => {
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<Link
|
||||
href={`/contributor/schedule/press-conference/detail/${row.original.id}`}
|
||||
href={`/contributor/schedule/live-report/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" />
|
||||
|
|
@ -159,14 +190,17 @@ const useTableColumns = () => {
|
|||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/contributor/schedule/press-conference/update/${row.original.id}`}
|
||||
href={`/contributor/schedule/live-report/update/${row.original.id}?scheduleType=${props.selectedTypeSchedule}`}
|
||||
>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<SquarePen className="w-4 h-4 me-1.5" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
||||
<DropdownMenuItem
|
||||
onClick={() => doDelete(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>
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ const LiveReportTable = () => {
|
|||
const [search, setSearch] = React.useState<string>("");
|
||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||
const [selectedType, setSelectedType] = React.useState<string>("1");
|
||||
const columns = useTableColumns();
|
||||
const columns = useTableColumns({ selectedTypeSchedule: selectedType });
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
|
|
@ -197,11 +197,11 @@ const LiveReportTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex flex-row items-center gap-3">
|
||||
<div className="">
|
||||
<div className="grid grid-cols-2 w-full md:w-fit md:flex lg:flex-row items-center gap-3">
|
||||
<div className="w-full md:w-fit">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
<Button size="md" variant="outline" className="w-full">
|
||||
1 - {showData} Data
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
@ -226,12 +226,12 @@ const LiveReportTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="border border-black rounded-md">
|
||||
<div className="border border-black rounded-md w-full md:w-fit">
|
||||
<Select
|
||||
value={selectedType}
|
||||
onValueChange={(value) => setSelectedType(value)}
|
||||
>
|
||||
<SelectTrigger className="w-[150px] text-black">
|
||||
<SelectTrigger className="w-full md:w-[150px] text-black">
|
||||
<SelectValue placeholder="Tipe" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
|
@ -244,10 +244,14 @@ const LiveReportTable = () => {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="w-full md:w-fit">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto" size="md">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full md:ml-auto"
|
||||
size="md"
|
||||
>
|
||||
Filter <ChevronDown />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
@ -287,10 +291,14 @@ const LiveReportTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center w-full md:w-fit">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto" size="md">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="md:ml-auto w-full"
|
||||
size="md"
|
||||
>
|
||||
Columns <ChevronDown />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
"use client";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTask from "@/components/form/task/task-form";
|
||||
import FormPressConference from "@/components/form/schedule/press-conference-form";
|
||||
import FormDetailPressConference from "@/components/form/schedule/press-conference-detail-form";
|
||||
import { useParams } from "next/navigation";
|
||||
import { id } from "date-fns/locale";
|
||||
import FormDetailLiveReport from "@/components/form/schedule/live-report-detail-form";
|
||||
|
||||
const LiveReportDetailPage = () => {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
"use client";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTask from "@/components/form/task/task-form";
|
||||
import FormPressConference from "@/components/form/schedule/press-conference-form";
|
||||
import FormDetailPressConference from "@/components/form/schedule/press-conference-detail-form";
|
||||
import { useParams } from "next/navigation";
|
||||
import { id } from "date-fns/locale";
|
||||
import FormUpdatePressConference from "@/components/form/schedule/press-conference-update-form";
|
||||
import FormUpdateLiveReport from "@/components/form/schedule/live-report-update-form";
|
||||
|
||||
const LiveReportUpdatePage = () => {
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ const useTableColumns = () => {
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
{roleId == 11 && (
|
||||
{(roleId == 11 || roleId == 12) && (
|
||||
<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" />
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ const TaskTaTable = () => {
|
|||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [showData, setShowData] = React.useState("50");
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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 { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -13,6 +13,8 @@ import { Button } from "@/components/ui/button";
|
|||
import { format } from "date-fns";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
|
|
@ -120,9 +122,27 @@ const columns: ColumnDef<any>[] = [
|
|||
Ditolak: "text-red-500 border-red-500",
|
||||
};
|
||||
|
||||
const colors = [
|
||||
"text-orange-500 border-orange-500",
|
||||
"text-orange-500 border-orange-500",
|
||||
"text-green-500 border-green-500",
|
||||
"text-blue-500 border-blue-500",
|
||||
"text-red-500 border-red-500",
|
||||
];
|
||||
|
||||
const status =
|
||||
Number(row.original?.statusId) == 2 &&
|
||||
row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) &&
|
||||
Number(row.original?.creatorGroupLevelId) != Number(userLevelId)
|
||||
? "1"
|
||||
: row.original?.statusId;
|
||||
|
||||
// Mendapatkan kelas warna dari mapping, default ke abu-abu jika tidak ditemukan
|
||||
const buttonClass =
|
||||
colorMapping[statusName] || "text-gray-500 border-gray-500";
|
||||
colors[Number(status)] || "text-gray-500 border-gray-500";
|
||||
// const buttonClass =
|
||||
// colorMapping[statusName] || "text-gray-500 border-gray-500";
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -131,7 +151,18 @@ const columns: ColumnDef<any>[] = [
|
|||
variant="outline"
|
||||
className={`btn btn-sm pill-btn ml-1 ${buttonClass}`}
|
||||
>
|
||||
{statusName || "Tidak Diketahui"}
|
||||
{(Number(row.original?.statusId) == 2 &&
|
||||
!row.original?.reviewedAtLevel !== null &&
|
||||
!row.original?.reviewedAtLevel?.includes(
|
||||
`:${Number(userLevelId)}:`
|
||||
) &&
|
||||
Number(row.original?.creatorGroupLevelId) !=
|
||||
Number(userLevelId)) ||
|
||||
(Number(row.original?.statusId) == 1 &&
|
||||
Number(row.original?.needApprovalFromLevel) ==
|
||||
Number(userLevelId))
|
||||
? "Menunggu Review"
|
||||
: row.original?.statusName}{" "}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { close, error, loading, successCallback } from "@/config/swal";
|
||||
import { checkWishlistStatus, deleteWishlist, getInfoProfile, mediaWishlist, saveWishlist } from "@/service/landing/landing";
|
||||
import {
|
||||
checkWishlistStatus,
|
||||
deleteWishlist,
|
||||
getInfoProfile,
|
||||
mediaWishlist,
|
||||
saveWishlist,
|
||||
} from "@/service/landing/landing";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
|
@ -13,7 +19,11 @@ import withReactContent from "sweetalert2-react-content";
|
|||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import Swal from "sweetalert2";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||
|
|
@ -59,11 +69,23 @@ const Galery = (props: any) => {
|
|||
}, [page, category, title]);
|
||||
|
||||
async function getDataVideo() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
const response = await mediaWishlist("2", isInstitute ? instituteId : "", name, filter, "9", pages, sortBy, format);
|
||||
const response = await mediaWishlist(
|
||||
"2",
|
||||
isInstitute ? instituteId : "",
|
||||
name,
|
||||
filter,
|
||||
"9",
|
||||
pages,
|
||||
sortBy,
|
||||
format
|
||||
);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentVideo(response?.data?.data?.content);
|
||||
|
|
@ -92,12 +114,24 @@ const Galery = (props: any) => {
|
|||
}, [page, category, title]);
|
||||
|
||||
async function getDataDocument() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("3", isInstitute ? instituteId : "", name, filter, "12", pages, sortBy, format);
|
||||
const response = await mediaWishlist(
|
||||
"3",
|
||||
isInstitute ? instituteId : "",
|
||||
name,
|
||||
filter,
|
||||
"12",
|
||||
pages,
|
||||
sortBy,
|
||||
format
|
||||
);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentDocument(response?.data?.data?.content);
|
||||
|
|
@ -117,12 +151,24 @@ const Galery = (props: any) => {
|
|||
}, [change, refresh]);
|
||||
|
||||
async function getDataAudio() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("4", isInstitute ? instituteId : "", name, filter, "6", pages, sortBy, format);
|
||||
const response = await mediaWishlist(
|
||||
"4",
|
||||
isInstitute ? instituteId : "",
|
||||
name,
|
||||
filter,
|
||||
"6",
|
||||
pages,
|
||||
sortBy,
|
||||
format
|
||||
);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentAudio(response?.data?.data?.content);
|
||||
|
|
@ -138,12 +184,24 @@ const Galery = (props: any) => {
|
|||
}, [page, category, title, refresh]);
|
||||
|
||||
async function getDataImage() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("1", isInstitute ? instituteId : "", name, filter, "12", pages, sortBy, format);
|
||||
const response = await mediaWishlist(
|
||||
"1",
|
||||
isInstitute ? instituteId : "",
|
||||
name,
|
||||
filter,
|
||||
"12",
|
||||
pages,
|
||||
sortBy,
|
||||
format
|
||||
);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentImage(response?.data?.data?.content);
|
||||
|
|
@ -225,7 +283,9 @@ const Galery = (props: any) => {
|
|||
};
|
||||
|
||||
const copyToClip = async (url: any) => {
|
||||
await navigator.clipboard.writeText(`https://mediahub.polri.go.id/video/detail/${url}`);
|
||||
await navigator.clipboard.writeText(
|
||||
`https://mediahub.polri.go.id/video/detail/${url}`
|
||||
);
|
||||
setCopySuccess("Copied");
|
||||
// toast.success("Link Berhasil Di Copy");
|
||||
};
|
||||
|
|
@ -283,7 +343,7 @@ const Galery = (props: any) => {
|
|||
<SidebarManagement />
|
||||
<div className="w-full lg:w-2/3 p-8 lg:p-0 mt-12">
|
||||
<div className="flex flex-col mt-4">
|
||||
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col lg:flex-row gap-5 mb-4">
|
||||
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col gap-5 mb-4">
|
||||
<h1 className="text-2xl w-fit font-bold bg-[#bb3523] px-4 py-1 rounded-lg text-center text-white">
|
||||
{/* <span className="text-black">Galeri </span>
|
||||
Saya */}
|
||||
|
|
@ -308,21 +368,27 @@ const Galery = (props: any) => {
|
|||
>
|
||||
{t("image")}
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">
|
||||
|
|
||||
</div>
|
||||
<TabsTrigger
|
||||
value="video"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
{t("video")}
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">
|
||||
|
|
||||
</div>
|
||||
<TabsTrigger
|
||||
value="text"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
{t("text")}
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">
|
||||
|
|
||||
</div>
|
||||
<TabsTrigger
|
||||
value="audio"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
|
|
@ -338,54 +404,110 @@ const Galery = (props: any) => {
|
|||
contentVideo?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentVideo?.map((video: any) => (
|
||||
<Card key={video?.id} className="hover:scale-105 transition-transform duration-300">
|
||||
<Card
|
||||
key={video?.id}
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
||||
<div>
|
||||
<div className="relative group overflow-hidden shadow-md hover:shadow-lg">
|
||||
<div className="relative h-60 rounded-lg overflow-hidden">
|
||||
<ImageBlurry src={video?.mediaUpload?.thumbnailLink} alt={video?.mediaUpload?.title} style={{ objectFit: "cover", width: "100%", height: "100%" }} />
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-transparent via-gray-900/80 to-transparent text-white">
|
||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||
<p className="text-sm p-2 lg:text-base font-semibold truncate">{video?.mediaUpload?.title}</p>
|
||||
<ImageBlurry
|
||||
src={video?.mediaUpload?.thumbnailLink}
|
||||
alt={video?.mediaUpload?.title}
|
||||
style={{
|
||||
objectFit: "cover",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black text-white">
|
||||
<Link
|
||||
href={`/video/detail/${video?.mediaUpload?.slug}`}
|
||||
>
|
||||
<p className="text-sm p-2 lg:text-base font-semibold truncate">
|
||||
{video?.mediaUpload?.title}
|
||||
</p>
|
||||
</Link>
|
||||
<p className="flex text-[10px] ml-1 mb-2 items-end">
|
||||
<p className="flex text-[10px] mr-1 mb-2 items-center justify-end self-end">
|
||||
<Popover>
|
||||
<PopoverTrigger className="flex cursor-pointer" asChild>
|
||||
<PopoverTrigger
|
||||
className="flex cursor-pointer"
|
||||
asChild
|
||||
>
|
||||
<a className="flex justify-end items-end place-items-end">
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
<Icon
|
||||
className="text-white ml-1"
|
||||
fontSize={25}
|
||||
icon="tabler:dots"
|
||||
/>
|
||||
</a>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div onClick={() => handleSaveWishlist(video?.mediaUpload?.id)} className="cursor-pointer flex flex-row gap-2 hover:text-red-800">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">{t("save")}</p>
|
||||
</div>
|
||||
<Link href={`/content-management/rewrite/create/${video?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
{/* <Link href={`/content-management/rewrite/create/${video?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
<Icon icon="jam:write" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">Content Rewrite</p>
|
||||
</Link>
|
||||
</Link> */}
|
||||
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button className="w-full flex flex-row items-center gap-3">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold mb-1">{t("share")}</p>
|
||||
<Icon
|
||||
icon="oi:share"
|
||||
fontSize={20}
|
||||
/>
|
||||
<p className="text-base font-semibold mb-1">
|
||||
{t("share")}
|
||||
</p>
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="mb-2">{t("shareTo")}</h1>
|
||||
<h1 className="mb-2">
|
||||
{t("shareTo")}
|
||||
</h1>
|
||||
<div className="flex flex-col mb-2">
|
||||
<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")} />
|
||||
<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()}>
|
||||
<Button
|
||||
className="bg-blue-500 text-white p-2 w-fit rounded-lg"
|
||||
onClick={() => shareToEmail()}
|
||||
>
|
||||
{t("send")}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div
|
||||
onClick={() =>
|
||||
handleDelete(video?.id)
|
||||
}
|
||||
className="cursor-pointer flex flex-row gap-2 hover:text-red-800"
|
||||
>
|
||||
<Icon
|
||||
icon="iconamoon:trash"
|
||||
fontSize={25}
|
||||
/>
|
||||
<p className="text-base font-semibold mb-2">
|
||||
{t("delete")}
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</p>
|
||||
|
|
@ -399,16 +521,31 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<Image width={1920} height={1080} src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
<Image
|
||||
width={1920}
|
||||
height={1080}
|
||||
src="/assets/empty-data.png"
|
||||
alt="empty"
|
||||
className="h-52 w-52 my-4"
|
||||
/>
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "audio" ? (
|
||||
contentAudio?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 ">
|
||||
{contentAudio?.map((audio: any) => (
|
||||
<div key={audio?.id} className="flex flex-col sm:flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||
<div
|
||||
key={audio?.id}
|
||||
className="flex flex-col sm:flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="32"
|
||||
height="34"
|
||||
viewBox="0 0 32 34"
|
||||
fill="null"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
|
||||
fill="white"
|
||||
|
|
@ -416,8 +553,12 @@ const Galery = (props: any) => {
|
|||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col object-cover flex-1">
|
||||
<Link href={`/audio/detail/${audio?.mediaUpload?.slug}`}>
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.mediaUpload?.slug}`}
|
||||
>
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
|
||||
{audio?.mediaUpload?.title}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
|
|
@ -425,9 +566,20 @@ const Galery = (props: any) => {
|
|||
<img src="/assets/wave.svg" className="w-80" />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center text-gray-500 dark:text-gray-400">
|
||||
<img src="/assets/audio-icon.png" alt="#" className="flex items-center justify-center" />
|
||||
<div className="flex mx-2 items-center justify-center">{audio?.mediaUpload?.duration}</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<img
|
||||
src="/assets/audio-icon.png"
|
||||
alt="#"
|
||||
className="flex items-center justify-center"
|
||||
/>
|
||||
<div className="flex mx-2 items-center justify-center">
|
||||
{audio?.mediaUpload?.duration}
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
|
|
@ -436,42 +588,69 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
</div>
|
||||
<Popover>
|
||||
<PopoverTrigger className="flex cursor-pointer" asChild>
|
||||
<PopoverTrigger
|
||||
className="flex cursor-pointer"
|
||||
asChild
|
||||
>
|
||||
<a className="flex justify-end items-end place-items-end">
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
<Icon
|
||||
className="text-white ml-1"
|
||||
fontSize={25}
|
||||
icon="tabler:dots"
|
||||
/>
|
||||
</a>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div onClick={() => handleSaveWishlist(audio?.mediaUpload?.id)} className="cursor-pointer flex flex-row gap-2 hover:text-red-800">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">{t("save")}</p>
|
||||
</div>
|
||||
<Link href={`/content-management/rewrite/create/${audio?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
<Icon icon="jam:write" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">Content Rewrite</p>
|
||||
</Link>
|
||||
{/* <Link href={`/content-management/rewrite/create/${video?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
<Icon icon="jam:write" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">Content Rewrite</p>
|
||||
</Link> */}
|
||||
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button className="w-full flex flex-row items-center gap-3">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold mb-1">{t("share")}</p>
|
||||
<p className="text-base font-semibold mb-1">
|
||||
{t("share")}
|
||||
</p>
|
||||
</button>
|
||||
</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")} />
|
||||
<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()}>
|
||||
<Button
|
||||
className="bg-blue-500 text-white p-2 w-fit rounded-lg"
|
||||
onClick={() => shareToEmail()}
|
||||
>
|
||||
{t("send")}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div
|
||||
onClick={() => handleDelete(audio?.id)}
|
||||
className="cursor-pointer flex flex-row gap-2 hover:text-red-800"
|
||||
>
|
||||
<Icon icon="iconamoon:trash" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">
|
||||
{t("delete")}
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
|
@ -479,61 +658,123 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<Image width={1920} height={1080} src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
<Image
|
||||
width={1920}
|
||||
height={1080}
|
||||
src="/assets/empty-data.png"
|
||||
alt="empty"
|
||||
className="h-52 w-52 my-4"
|
||||
/>
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "image" ? (
|
||||
contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentImage?.map((image: any) => (
|
||||
<Card key={image?.id} className="hover:scale-105 transition-transform duration-300">
|
||||
<Card
|
||||
key={image?.id}
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
||||
<div>
|
||||
<div className="relative group overflow-hidden shadow-md hover:shadow-lg">
|
||||
<div className="relative h-60 rounded-lg overflow-hidden">
|
||||
<ImageBlurry src={image?.mediaUpload?.thumbnailLink} alt={image?.mediaUpload?.title} style={{ objectFit: "cover", width: "100%", height: "100%" }} />
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-transparent via-gray-900/80 to-transparent text-white">
|
||||
<Link href={`/video/detail/${image?.mediaUpload?.slug}`}>
|
||||
<p className="text-sm p-2 lg:text-base font-semibold truncate">{image?.mediaUpload?.title}</p>
|
||||
<ImageBlurry
|
||||
src={image?.mediaUpload?.thumbnailLink}
|
||||
alt={image?.mediaUpload?.title}
|
||||
style={{
|
||||
objectFit: "cover",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black text-white">
|
||||
<Link
|
||||
href={`/video/detail/${image?.mediaUpload?.slug}`}
|
||||
>
|
||||
<p className="text-sm p-2 lg:text-base font-semibold truncate">
|
||||
{image?.mediaUpload?.title}
|
||||
</p>
|
||||
</Link>
|
||||
<p className="flex text-[10px] ml-1 mb-2 items-end">
|
||||
<p className="flex text-[10px] mr-1 mb-2 items-center justify-end self-end">
|
||||
<Popover>
|
||||
<PopoverTrigger className="flex cursor-pointer" asChild>
|
||||
<PopoverTrigger
|
||||
className="flex cursor-pointer"
|
||||
asChild
|
||||
>
|
||||
<a className="flex justify-end items-end place-items-end">
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
<Icon
|
||||
className="text-white ml-1"
|
||||
fontSize={25}
|
||||
icon="tabler:dots"
|
||||
/>
|
||||
</a>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div onClick={() => handleSaveWishlist(image?.mediaUpload?.id)} className="cursor-pointer flex flex-row gap-2 hover:text-red-800">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">{t("save")}</p>
|
||||
</div>
|
||||
<Link href={`/content-management/rewrite/create/${image?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
{/* <Link href={`/content-management/rewrite/create/${video?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
<Icon icon="jam:write" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">Content Rewrite</p>
|
||||
</Link>
|
||||
</Link> */}
|
||||
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button className="w-full flex flex-row items-center gap-3">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold mb-1"> {t("share")}</p>
|
||||
<Icon
|
||||
icon="oi:share"
|
||||
fontSize={20}
|
||||
/>
|
||||
<p className="text-base font-semibold mb-1">
|
||||
{t("share")}
|
||||
</p>
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="mb-2">{t("shareTo")}</h1>
|
||||
<h1 className="mb-2">
|
||||
{t("shareTo")}
|
||||
</h1>
|
||||
<div className="flex flex-col mb-2">
|
||||
<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")} />
|
||||
<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()}>
|
||||
<Button
|
||||
className="bg-blue-500 text-white p-2 w-fit rounded-lg"
|
||||
onClick={() => shareToEmail()}
|
||||
>
|
||||
{t("send")}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div
|
||||
onClick={() =>
|
||||
handleDelete(image?.id)
|
||||
}
|
||||
className="cursor-pointer flex flex-row gap-2 hover:text-red-800"
|
||||
>
|
||||
<Icon
|
||||
icon="iconamoon:trash"
|
||||
fontSize={25}
|
||||
/>
|
||||
<p className="text-base font-semibold mb-2">
|
||||
{t("delete")}
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</p>
|
||||
|
|
@ -547,15 +788,30 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<Image width={1920} height={1080} src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
<Image
|
||||
width={1920}
|
||||
height={1080}
|
||||
src="/assets/empty-data.png"
|
||||
alt="empty"
|
||||
className="h-52 w-52 my-4"
|
||||
/>
|
||||
</p>
|
||||
)
|
||||
) : contentDocument.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{contentDocument?.map((document: any) => (
|
||||
<div key={document?.id} className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||
<div
|
||||
key={document?.id}
|
||||
className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="28"
|
||||
height="34"
|
||||
viewBox="0 0 28 34"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
|
||||
fill="black"
|
||||
|
|
@ -564,53 +820,88 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 gap-2">
|
||||
<Link href={`/document/detail/${document?.mediaUpload?.slug}`} className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
|
||||
<Link
|
||||
href={`/document/detail/${document?.mediaUpload?.slug}`}
|
||||
className="font-semibold text-gray-900 dark:text-white mt-1 text-sm"
|
||||
>
|
||||
{document?.mediaUpload?.title}
|
||||
</Link>
|
||||
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
||||
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z"
|
||||
/>
|
||||
</svg>
|
||||
Download {t("document")}
|
||||
</div>
|
||||
</div>
|
||||
<Popover>
|
||||
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||
<a className="flex justify-end items-end place-items-end">
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
<PopoverTrigger className="flex cursor-pointer" asChild>
|
||||
<a className="flex items-center justify-end self-end">
|
||||
<Icon
|
||||
className="text-white ml-1"
|
||||
fontSize={25}
|
||||
icon="tabler:dots"
|
||||
/>
|
||||
</a>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div onClick={() => handleSaveWishlist(document?.mediaUpload?.id)} className="cursor-pointer flex flex-row gap-2 hover:text-red-800">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">{t("save")}</p>
|
||||
</div>
|
||||
<Link href={`/content-management/rewrite/create/${document?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
<Icon icon="jam:write" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">Content Rewrite</p>
|
||||
</Link>
|
||||
{/* <Link href={`/content-management/rewrite/create/${video?.mediaUpload?.id}`} className="flex flex-row hover:text-red-800 gap-2">
|
||||
<Icon icon="jam:write" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">Content Rewrite</p>
|
||||
</Link> */}
|
||||
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button className="w-full flex items-center gap-2">
|
||||
<button className="w-full flex flex-row items-center gap-3">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold mb-2">{t("share")}</p>
|
||||
<p className="text-base font-semibold mb-1">
|
||||
{t("share")}
|
||||
</p>
|
||||
</button>
|
||||
</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")} />
|
||||
<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()}>
|
||||
<Button
|
||||
className="bg-blue-500 text-white p-2 w-fit rounded-lg"
|
||||
onClick={() => shareToEmail()}
|
||||
>
|
||||
{t("send")}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div
|
||||
onClick={() => handleDelete(document?.id)}
|
||||
className="cursor-pointer flex flex-row gap-2 hover:text-red-800"
|
||||
>
|
||||
<Icon icon="iconamoon:trash" fontSize={25} />
|
||||
<p className="text-base font-semibold mb-2">
|
||||
{t("delete")}
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
|
@ -618,7 +909,13 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<Image width={1920} height={1080} src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
<Image
|
||||
width={1920}
|
||||
height={1080}
|
||||
src="/assets/empty-data.png"
|
||||
alt="empty"
|
||||
className="h-52 w-52 my-4"
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ const Galery = (props: any) => {
|
|||
<SidebarManagement />
|
||||
<div className="w-full lg:w-2/3 p-8 lg:p-0 mt-12">
|
||||
<div className="flex flex-col mt-4">
|
||||
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col lg:flex-row gap-5 mb-4">
|
||||
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col gap-5 mb-4">
|
||||
<h1 className="text-2xl w-fit font-bold bg-[#bb3523] px-4 py-1 rounded-lg text-center text-white">
|
||||
<span className="text-black">{t("gallery")} </span>
|
||||
{profile?.institute?.name}
|
||||
|
|
@ -337,14 +337,14 @@ const Galery = (props: any) => {
|
|||
<div className="relative group overflow-hidden shadow-md hover:shadow-lg">
|
||||
<div className="relative h-60 rounded-lg overflow-hidden">
|
||||
<ImageBlurry src={video?.mediaUpload?.thumbnailLink} alt={video?.mediaUpload?.title} style={{ objectFit: "cover", width: "100%", height: "100%" }} />
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-transparent via-gray-900/80 to-transparent text-white">
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black text-white">
|
||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||
<p className="text-sm p-2 lg:text-base font-semibold truncate">{video?.mediaUpload?.title}</p>
|
||||
</Link>
|
||||
<p className="flex text-[10px] ml-1 mb-2 items-end">
|
||||
<p className="flex text-[10px] mr-1 mb-2 items-center justify-end self-end">
|
||||
<Popover>
|
||||
<PopoverTrigger className="flex cursor-pointer" asChild>
|
||||
<a className="flex justify-end items-end place-items-end">
|
||||
<a className="flex justify-end items-center">
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
</a>
|
||||
</PopoverTrigger>
|
||||
|
|
@ -483,11 +483,11 @@ const Galery = (props: any) => {
|
|||
<div className="relative group overflow-hidden shadow-md hover:shadow-lg">
|
||||
<div className="relative h-60 rounded-lg overflow-hidden">
|
||||
<ImageBlurry src={image?.mediaUpload?.thumbnailLink} alt={image?.mediaUpload?.title} style={{ objectFit: "cover", width: "100%", height: "100%" }} />
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-transparent via-gray-900/80 to-transparent text-white">
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black text-white">
|
||||
<Link href={`/video/detail/${image?.mediaUpload?.slug}`}>
|
||||
<p className="text-sm p-2 lg:text-base font-semibold truncate">{image?.mediaUpload?.title}</p>
|
||||
</Link>
|
||||
<p className="flex text-[10px] ml-1 mb-2 items-end">
|
||||
<p className="flex text-[10px] mr-1 mb-2 items-center justify-end self-end">
|
||||
<Popover>
|
||||
<PopoverTrigger className="flex cursor-pointer" asChild>
|
||||
<a className="flex justify-end items-end place-items-end">
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import {
|
|||
import { Controller, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import CustomEditor from "@/components/editor/custom-editor";
|
||||
import {
|
||||
generateDataArticle,
|
||||
generateDataRewrite,
|
||||
|
|
@ -37,6 +36,7 @@ import Cookies from "js-cookie";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useTranslations } from "next-intl";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -51,6 +51,12 @@ const imageSchema = z.object({
|
|||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
const page = (props: any) => {
|
||||
const { states } = props;
|
||||
|
|
@ -90,9 +96,6 @@ const page = (props: any) => {
|
|||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string().required("Judul tidak boleh kosong"),
|
||||
mainKeyword: Yup.string().required("Keyword tidak boleh kosong"),
|
||||
seo: Yup.string().required(
|
||||
"Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter"
|
||||
),
|
||||
description: Yup.string().required(
|
||||
"Narasi Penugasan harus lebih dari 2 karakter."
|
||||
),
|
||||
|
|
@ -172,6 +175,7 @@ const page = (props: any) => {
|
|||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
loading();
|
||||
setLoadingState(true);
|
||||
const response = await getDetail(
|
||||
id,
|
||||
|
|
@ -187,7 +191,7 @@ const page = (props: any) => {
|
|||
if (componentMounted) {
|
||||
setValue("title", response?.data?.data?.title);
|
||||
setValue("mainKeyword", response?.data?.data?.title);
|
||||
setValue("seo", response?.data?.data?.description);
|
||||
setValue("description", response?.data?.data?.htmlDescription);
|
||||
setContent(response?.data?.data);
|
||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||
setListSuggestion(responseGet?.data?.data);
|
||||
|
|
@ -221,6 +225,7 @@ const page = (props: any) => {
|
|||
setLoadingState(false); // (2) write some value to state
|
||||
// await new Promise(resolve => setTimeout(resolve, 1000)); // 2 sec
|
||||
// setIdm();
|
||||
close();
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
|
@ -235,7 +240,7 @@ const page = (props: any) => {
|
|||
const handleGenerateArtikel = async () => {
|
||||
loading();
|
||||
const request = {
|
||||
style: "friendly",
|
||||
style: "Professional",
|
||||
lang: "id",
|
||||
contextType: "text",
|
||||
urlContext: null,
|
||||
|
|
@ -295,12 +300,13 @@ const page = (props: any) => {
|
|||
/<img[^>]*>/g,
|
||||
""
|
||||
);
|
||||
console.log("lalalala", cleanArticleBody);
|
||||
console.log("lalalala", articleData);
|
||||
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||
setValue("description", cleanArticleBody || "");
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
setDetailArticle(articleData);
|
||||
setSelectedArticleId(id);
|
||||
// setSelectedArticleId(id);
|
||||
console.log("idnya", id);
|
||||
setArticleImages(articleImagesData || []);
|
||||
} catch (error) {
|
||||
console.error("Error fetching article details:", error);
|
||||
|
|
@ -454,18 +460,22 @@ const page = (props: any) => {
|
|||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="font-semibold">SEO</p>
|
||||
<p className="font-semibold">Deskripsi</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="seo"
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Textarea
|
||||
className="py-20"
|
||||
id="seo"
|
||||
placeholder="Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter"
|
||||
{...register("seo")}
|
||||
// <Textarea
|
||||
// className="py-20"
|
||||
// id="description"
|
||||
// placeholder="Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter"
|
||||
// {...register("description")}
|
||||
// onChange={onChange}
|
||||
// value={value}
|
||||
// />
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
initialData={content?.htmlDescription}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -482,14 +492,17 @@ const page = (props: any) => {
|
|||
{articleIds.map((id: any, index: any) => (
|
||||
<p
|
||||
key={index}
|
||||
className={`text-black border border-black w-fit rounded-md p-3 m-1 ${
|
||||
selectedArticleId === id
|
||||
? "text-[#31ce36] w-fit p-3 hover:bg-[#31ce36] hover:text-white cursor-pointer border border-[#31ce36]"
|
||||
: "text-[#48abf7] p-3 hover:text-white hover:bg-[#48abf7] w-fit cursor-pointer border border-[#48abf7]"
|
||||
className={` w-fit rounded-md p-3 m-1 ${
|
||||
Number(selectedArticleId) === Number(id)
|
||||
? "text-white w-fit p-3 hover:text-white cursor-pointer bg-[#31ce36] border-[#31ce36]"
|
||||
: "text-[#48abf7] p-3 hover:text-white hover:bg-[#48abf7] w-fit cursor-pointer border border-[#48abf7]"
|
||||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
onClick={() => {
|
||||
handleArticleIdClick(id);
|
||||
setSelectedArticleId(Number(id));
|
||||
}}
|
||||
>
|
||||
{id}
|
||||
Article {index + 1}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ const page = () => {
|
|||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-transparent via-gray-900/80 to-transparent text-white">
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black text-white">
|
||||
<Link
|
||||
href={`/content-management/rewrite/detail/${image.id}`}
|
||||
>
|
||||
|
|
@ -251,7 +251,7 @@ const page = () => {
|
|||
{image?.title}
|
||||
</p>
|
||||
</Link>
|
||||
<p className="flex text-[10px] ml-1 mb-2 items-end">
|
||||
<p className="flex text-[10px] mr-1 mb-2 items-center justify-end self-end">
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
className="flex cursor-pointer"
|
||||
|
|
@ -266,7 +266,7 @@ const page = () => {
|
|||
</a>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div
|
||||
{/* <div
|
||||
onClick={() =>
|
||||
handleSaveWishlist(
|
||||
image?.mediaUpload?.id
|
||||
|
|
@ -281,8 +281,8 @@ const page = () => {
|
|||
<p className="text-base font-semibold mb-2">
|
||||
{t("save")}
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
</div> */}
|
||||
{/* <Link
|
||||
href={`/content-management/rewrite/create/${image?.mediaUploadId}`}
|
||||
className="flex flex-row hover:text-red-800 gap-2"
|
||||
>
|
||||
|
|
@ -290,7 +290,7 @@ const page = () => {
|
|||
<p className="text-base font-semibold mb-2">
|
||||
Content Rewrite
|
||||
</p>
|
||||
</Link>
|
||||
</Link> */}
|
||||
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
|
|
@ -336,6 +336,22 @@ const page = () => {
|
|||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div
|
||||
onClick={() =>
|
||||
handleDelete(
|
||||
image?.id
|
||||
)
|
||||
}
|
||||
className="cursor-pointer flex flex-row gap-2 hover:text-red-800"
|
||||
>
|
||||
<Icon
|
||||
icon="iconamoon:trash"
|
||||
fontSize={25}
|
||||
/>
|
||||
<p className="text-base font-semibold mb-2">
|
||||
{t("delete")}
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ const Schedule = (props: any) => {
|
|||
return (
|
||||
<>
|
||||
{/* Awal Komponen Kiri */}
|
||||
<div className="relative pl-4 lg:px-8 lg:py-10 py-4 bg-[#f7f7f7] dark:bg-slate-800">
|
||||
<div className="relative pl-4 lg:px-8 lg:py-10 py-4 bg-[#f7f7f7] dark:bg-slate-800 min-h-[80vh]">
|
||||
<div className="flex flex-row items-center">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
|
|
@ -652,7 +652,7 @@ const Schedule = (props: any) => {
|
|||
<div className="h-[500px] overflow-y-auto md:overflow-y-auto w-full lg:w-3/4 ">
|
||||
<div className="container-fluid relative">
|
||||
<div className="grid grid-cols-1 mt-8">
|
||||
<div className="relative block bg-white w-max dark:bg-slate-900">
|
||||
<div className="relative block bg-white w-full dark:bg-slate-900 mx-auto">
|
||||
<table className="w-full text-sm text-start">
|
||||
<thead className="text-md">
|
||||
<tr className="h-full">
|
||||
|
|
@ -846,7 +846,7 @@ const Schedule = (props: any) => {
|
|||
</div>
|
||||
|
||||
{/* komponen Kanan */}
|
||||
<div className="w-full lg:w-1/4 flex flex-col gap-6">
|
||||
<div className="w-full lg:w-1/4 flex flex-col gap-6 ml-4">
|
||||
<div className="relative text-gray-600 dark:text-white">
|
||||
<input
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ import {
|
|||
rejectFiles,
|
||||
submitApproval,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import {
|
||||
detailMedia,
|
||||
getDataApprovalByMediaUpload,
|
||||
} from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { MailIcon, Music } from "lucide-react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
|
@ -58,6 +61,9 @@ import dynamic from "next/dynamic";
|
|||
import WavesurferPlayer from "@wavesurfer/react";
|
||||
import WaveSurfer from "wavesurfer.js";
|
||||
import { useTranslations } from "next-intl";
|
||||
import SuggestionModal from "@/components/modal/suggestions-modal";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -148,6 +154,7 @@ export default function FormAudioDetail() {
|
|||
|
||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [approval, setApproval] = useState<any>();
|
||||
|
||||
const onReady = (ws: any) => {
|
||||
setWavesurfer(ws);
|
||||
|
|
@ -281,9 +288,10 @@ export default function FormAudioDetail() {
|
|||
file.secondaryUrl ? file.secondaryUrl : "default-audio.mp3"
|
||||
);
|
||||
|
||||
console.log("audio", fileUrls);
|
||||
|
||||
setDetailThumb(fileUrls);
|
||||
|
||||
const approvals = await getDataApprovalByMediaUpload(details?.id);
|
||||
setApproval(approvals?.data?.data);
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -308,19 +316,20 @@ export default function FormAudioDetail() {
|
|||
Number(status) == 2 ||
|
||||
Number(status) == 4
|
||||
) {
|
||||
MySwal.fire({
|
||||
title: "Simpan Approval",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save();
|
||||
}
|
||||
});
|
||||
save();
|
||||
|
||||
// MySwal.fire({
|
||||
// title: "Simpan Approval",
|
||||
// text: "",
|
||||
// icon: "warning",
|
||||
// showCancelButton: true,
|
||||
// cancelButtonColor: "#d33",
|
||||
// confirmButtonColor: "#3085d6",
|
||||
// confirmButtonText: "Simpan",
|
||||
// }).then((result) => {
|
||||
// if (result.isConfirmed) {
|
||||
// }
|
||||
// });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -345,7 +354,7 @@ export default function FormAudioDetail() {
|
|||
// files: [],
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
};
|
||||
|
||||
setModalOpen(false);
|
||||
loading();
|
||||
const response = await submitApproval(data);
|
||||
|
||||
|
|
@ -560,7 +569,7 @@ export default function FormAudioDetail() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[600px]">
|
||||
<Card className="pb-3">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
|
|
@ -639,13 +648,21 @@ export default function FormAudioDetail() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||
<MailIcon />
|
||||
<p className="">{t("suggestion-box")}(0)</p>
|
||||
</div>
|
||||
<SuggestionModal
|
||||
id={Number(id)}
|
||||
numberOfSuggestion={detail?.numberOfSuggestion}
|
||||
/>
|
||||
<div className="px-3 py-3 border mx-3">
|
||||
<p>{t("information")}:</p>
|
||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||
<p>Komentar</p>
|
||||
<p>{approval?.message}</p>
|
||||
<p className="text-right text-sm">
|
||||
{" "}
|
||||
{approval?.approvalBy?.fullname} |{" "}
|
||||
{formatDateToIndonesian(approval?.approvalDate)}
|
||||
</p>
|
||||
<ApprovalHistoryModal id={Number(id)} />
|
||||
</div>
|
||||
{/* {detail?.isPublish == false ? (
|
||||
<div className="p-3">
|
||||
|
|
@ -654,39 +671,6 @@ export default function FormAudioDetail() {
|
|||
) : (
|
||||
""
|
||||
)} */}
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
|
||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||
<DialogContent size="md">
|
||||
|
|
@ -699,7 +683,18 @@ export default function FormAudioDetail() {
|
|||
key={file.id}
|
||||
className="flex flex-row gap-2 items-center"
|
||||
>
|
||||
<img src={file.url} className="w-[200px]" />
|
||||
{/* <img src={file.url} className="w-[200px]" /> */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>{" "}
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between text-sm">
|
||||
{file.fileName}
|
||||
|
|
@ -888,6 +883,39 @@ export default function FormAudioDetail() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</Card>
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -148,6 +148,9 @@ export default function FormAudio() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"audio/*": [],
|
||||
},
|
||||
});
|
||||
|
||||
const audioSchema = z.object({
|
||||
|
|
@ -668,7 +671,18 @@ export default function FormAudio() {
|
|||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="file-preview">{renderFilePreview(file)}</div>
|
||||
{/* <div className="file-preview">{renderFilePreview(file)}</div> */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>{" "}
|
||||
<div>
|
||||
<div className=" text-sm text-card-foreground">{file.name}</div>
|
||||
<div className=" text-xs font-light text-muted-foreground">
|
||||
|
|
@ -1015,12 +1029,12 @@ export default function FormAudio() {
|
|||
<Fragment>
|
||||
<div>{fileList}</div>
|
||||
<div className=" flex justify-between gap-2">
|
||||
<div className="flex flex-row items-center gap-3 py-3">
|
||||
{/* <div className="flex flex-row items-center gap-3 py-3">
|
||||
<Label>Gunakan Watermark</Label>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch defaultChecked color="primary" id="c2" />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<Button
|
||||
color="destructive"
|
||||
onClick={handleRemoveAllFiles}
|
||||
|
|
@ -1036,7 +1050,7 @@ export default function FormAudio() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[500px]">
|
||||
<Card className="pb-3">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ export default function FormAudioUpdate() {
|
|||
polres: false,
|
||||
});
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [prevFiles, setPrefFiles] = useState([]);
|
||||
|
||||
let fileTypeId = "4";
|
||||
|
||||
|
|
@ -155,6 +156,9 @@ export default function FormAudioUpdate() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"audio/*": [],
|
||||
},
|
||||
});
|
||||
const {
|
||||
control,
|
||||
|
|
@ -242,7 +246,8 @@ export default function FormAudioUpdate() {
|
|||
setDetail(details);
|
||||
|
||||
if (details?.files) {
|
||||
setFiles(details.files);
|
||||
setPrefFiles(details.files);
|
||||
// setFiles(details.files);
|
||||
const initialOptions: { [key: number]: string[] } = {};
|
||||
details.files.forEach((file: any) => {
|
||||
if (file.placements) {
|
||||
|
|
@ -505,11 +510,11 @@ export default function FormAudioUpdate() {
|
|||
}
|
||||
};
|
||||
|
||||
// const handleRemoveFile = (file: FileWithPreview) => {
|
||||
// const uploadedFiles = files;
|
||||
// const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
// setFiles([...filtered]);
|
||||
// };
|
||||
const handleRemoveFile = (file: FileWithPreview) => {
|
||||
const uploadedFiles = files;
|
||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
setFiles([...filtered]);
|
||||
};
|
||||
|
||||
const fileList = files.map((file: any) => (
|
||||
<div
|
||||
|
|
@ -517,7 +522,17 @@ export default function FormAudioUpdate() {
|
|||
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="file-preview">{renderFilePreview(file)}</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>{" "}
|
||||
<div>
|
||||
<div className="text-sm text-card-foreground">
|
||||
{file.fileName || file.name}
|
||||
|
|
@ -538,7 +553,7 @@ export default function FormAudioUpdate() {
|
|||
color="destructive"
|
||||
variant="outline"
|
||||
className="border-none rounded-full"
|
||||
onClick={() => handleDeleteFile(file.id)} // Kirim ID spesifik
|
||||
onClick={() => handleRemoveFile(file)} // Kirim ID spesifik
|
||||
>
|
||||
<Icon icon="tabler:x" className="h-5 w-5" />
|
||||
</Button>
|
||||
|
|
@ -723,12 +738,12 @@ export default function FormAudioUpdate() {
|
|||
<Fragment>
|
||||
<div>{fileList}</div>
|
||||
<div className=" flex justify-between gap-2">
|
||||
<div className="flex flex-row items-center gap-3 py-3">
|
||||
{/* <div className="flex flex-row items-center gap-3 py-3">
|
||||
<Label>{t("watermark")}</Label>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch defaultChecked color="primary" id="c2" />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* <Button
|
||||
color="destructive"
|
||||
onClick={handleRemoveAllFiles}
|
||||
|
|
@ -738,7 +753,59 @@ export default function FormAudioUpdate() {
|
|||
</div>
|
||||
</Fragment>
|
||||
) : null}
|
||||
{files.length > 0 && (
|
||||
{prevFiles?.length > 0 &&
|
||||
prevFiles.map((file: any) => (
|
||||
<div
|
||||
key={file.id} // Gunakan ID file sebagai key
|
||||
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>{" "}
|
||||
<div>
|
||||
<div className="text-sm text-card-foreground">
|
||||
{file.fileName || file.name}
|
||||
</div>
|
||||
<div className="text-xs font-light text-muted-foreground">
|
||||
{Math.round(file.size / 100) / 10 > 1000 ? (
|
||||
<>
|
||||
{(
|
||||
Math.round(file.size / 100) / 10000
|
||||
).toFixed(1)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{(Math.round(file.size / 100) / 10).toFixed(
|
||||
1
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{" kb"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="icon"
|
||||
color="destructive"
|
||||
variant="outline"
|
||||
className="border-none rounded-full"
|
||||
onClick={() => handleDeleteFile(file.id)} // Kirim ID spesifik
|
||||
>
|
||||
<Icon icon="tabler:x" className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{/* {files.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<Label className="text-lg font-semibold">
|
||||
{" "}
|
||||
|
|
@ -844,14 +911,14 @@ export default function FormAudioUpdate() {
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
</Fragment>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[800px]">
|
||||
<Card className="pb-3">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
|
|
@ -930,14 +997,14 @@ export default function FormAudioUpdate() {
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||
{/* <div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||
<MailIcon />
|
||||
<p className="">{t("suggestion-box")} (0)</p>
|
||||
</div>
|
||||
<div className="px-3 py-3">
|
||||
<p>{t("information")}:</p>
|
||||
{/* <p>{detail?.status}</p> */}
|
||||
</div>
|
||||
<p>{detail?.status}</p>
|
||||
</div> */}
|
||||
</Card>
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ import {
|
|||
rejectFiles,
|
||||
submitApproval,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import {
|
||||
detailMedia,
|
||||
getDataApprovalByMediaUpload,
|
||||
} from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { MailIcon } from "lucide-react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
|
@ -49,6 +52,7 @@ import {
|
|||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { close, loading, successCallback } from "@/config/swal";
|
||||
|
|
@ -59,6 +63,9 @@ import dynamic from "next/dynamic";
|
|||
import { useRouter } from "@/i18n/routing";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { UnitMapping } from "@/app/[locale]/(protected)/contributor/agenda-setting/unit-mapping";
|
||||
import SuggestionModal from "@/components/modal/suggestions-modal";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -139,6 +146,7 @@ export default function FormImageDetail() {
|
|||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||
const [approval, setApproval] = useState<any>();
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState("");
|
||||
const [files, setFiles] = useState<FileType[]>([]);
|
||||
|
|
@ -260,6 +268,9 @@ export default function FormImageDetail() {
|
|||
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
|
||||
);
|
||||
setDetailThumb(fileUrls);
|
||||
|
||||
const approvals = await getDataApprovalByMediaUpload(details?.id);
|
||||
setApproval(approvals?.data?.data);
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -322,6 +333,8 @@ export default function FormImageDetail() {
|
|||
message: description,
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
};
|
||||
|
||||
setModalOpen(false);
|
||||
loading();
|
||||
const response = await submitApproval(data);
|
||||
|
||||
|
|
@ -540,7 +553,7 @@ export default function FormImageDetail() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[1050px]">
|
||||
<Card className="pb-3">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
|
|
@ -628,13 +641,22 @@ export default function FormImageDetail() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||
<MailIcon />
|
||||
<p className="">{t("suggestion-box")} (0)</p>
|
||||
</div>
|
||||
|
||||
<SuggestionModal
|
||||
id={Number(id)}
|
||||
numberOfSuggestion={detail?.numberOfSuggestion}
|
||||
/>
|
||||
<div className="px-3 py-3 border mx-3">
|
||||
<p>{t("information")}:</p>
|
||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||
<p>Komentar</p>
|
||||
<p>{approval?.message}</p>
|
||||
<p className="text-right text-sm">
|
||||
{" "}
|
||||
{approval?.approvalBy?.fullname} |{" "}
|
||||
{formatDateToIndonesian(approval?.approvalDate)}
|
||||
</p>
|
||||
<ApprovalHistoryModal id={Number(id)} />
|
||||
</div>
|
||||
{/* {detail?.isPublish == false ? (
|
||||
<div className="p-3">
|
||||
|
|
@ -675,40 +697,6 @@ export default function FormImageDetail() {
|
|||
) : (
|
||||
""
|
||||
)} */}
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" />
|
||||
{t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
|
||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||
<DialogContent className="min-w-max h-[600px] overflow-y-auto">
|
||||
|
|
@ -919,6 +907,40 @@ export default function FormImageDetail() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</Card>
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" />
|
||||
{t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ export default function FormImage() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"image/*": [],
|
||||
},
|
||||
});
|
||||
|
||||
const imageSchema = z.object({
|
||||
|
|
@ -1020,12 +1023,12 @@ export default function FormImage() {
|
|||
<Fragment>
|
||||
<div>{fileList}</div>
|
||||
<div className=" flex justify-between gap-2">
|
||||
<div className="flex flex-row items-center gap-3 py-3">
|
||||
{/* <div className="flex flex-row items-center gap-3 py-3">
|
||||
<Label>{t("watermark")}</Label>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch defaultChecked color="primary" id="c2" />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<Button
|
||||
color="destructive"
|
||||
onClick={handleRemoveAllFiles}
|
||||
|
|
|
|||
|
|
@ -160,6 +160,9 @@ export default function FormImageUpdate() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"image/*": [],
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ import {
|
|||
rejectFiles,
|
||||
submitApproval,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import {
|
||||
detailMedia,
|
||||
getDataApprovalByMediaUpload,
|
||||
} from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { MailIcon } from "lucide-react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
|
@ -56,6 +59,9 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
|||
import { error } from "@/lib/swal";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTranslations } from "next-intl";
|
||||
import SuggestionModal from "@/components/modal/suggestions-modal";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -143,6 +149,8 @@ export default function FormTeksDetail() {
|
|||
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||
|
||||
const [approval, setApproval] = useState<any>();
|
||||
|
||||
let fileTypeId = "3";
|
||||
|
||||
const {
|
||||
|
|
@ -262,6 +270,9 @@ export default function FormTeksDetail() {
|
|||
fileName: file.fileName,
|
||||
}));
|
||||
setDetailThumb(fileUrls);
|
||||
|
||||
const approvals = await getDataApprovalByMediaUpload(details?.id);
|
||||
setApproval(approvals?.data?.data);
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -323,6 +334,7 @@ export default function FormTeksDetail() {
|
|||
message: description,
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
};
|
||||
setModalOpen(false);
|
||||
|
||||
loading();
|
||||
const response = await submitApproval(data);
|
||||
|
|
@ -571,7 +583,7 @@ export default function FormTeksDetail() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[800px]">
|
||||
<Card className="pb-3">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
|
|
@ -659,13 +671,21 @@ export default function FormTeksDetail() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||
<MailIcon />
|
||||
<p className="">{t("suggestion-box")} (0)</p>
|
||||
</div>
|
||||
<SuggestionModal
|
||||
id={Number(id)}
|
||||
numberOfSuggestion={detail?.numberOfSuggestion}
|
||||
/>
|
||||
<div className="px-3 py-3 border mx-3">
|
||||
<p>{t("information")}:</p>
|
||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||
<p>Komentar</p>
|
||||
<p>{approval?.message}</p>
|
||||
<p className="text-right text-sm">
|
||||
{" "}
|
||||
{approval?.approvalBy?.fullname} |{" "}
|
||||
{formatDateToIndonesian(approval?.approvalDate)}
|
||||
</p>
|
||||
<ApprovalHistoryModal id={Number(id)} />
|
||||
</div>
|
||||
{/* {detail?.isPublish == false ? (
|
||||
<div className="p-3">
|
||||
|
|
@ -674,39 +694,6 @@ export default function FormTeksDetail() {
|
|||
) : (
|
||||
""
|
||||
)} */}
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
|
||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||
<DialogContent size="md">
|
||||
|
|
@ -719,7 +706,18 @@ export default function FormTeksDetail() {
|
|||
key={file.id}
|
||||
className="flex flex-row gap-2 items-center"
|
||||
>
|
||||
<img src={file.url} className="w-[200px]" />
|
||||
{/* <img src={file.url} className="w-[200px]" /> */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5.414a1.5 1.5 0 0 0-.44-1.06L9.647 1.439A1.5 1.5 0 0 0 8.586 1zM4 3a1 1 0 0 1 1-1h3v2.5A1.5 1.5 0 0 0 9.5 6H12v7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm7.793 2H9.5a.5.5 0 0 1-.5-.5V2.207zM7 7.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M7.5 9a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zM7 11.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M5.5 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1M6 9.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0M5.5 12a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1"
|
||||
/>
|
||||
</svg>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between text-sm">
|
||||
{file.fileName}
|
||||
|
|
@ -908,6 +906,39 @@ export default function FormTeksDetail() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</Card>
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -147,6 +147,12 @@ export default function FormTeks() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"application/pdf": [],
|
||||
"application/msword": [], // .doc
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||
[], // .docx
|
||||
},
|
||||
});
|
||||
|
||||
const teksSchema = z.object({
|
||||
|
|
@ -1012,12 +1018,12 @@ export default function FormTeks() {
|
|||
<Fragment>
|
||||
<div>{fileList}</div>
|
||||
<div className=" flex justify-between gap-2">
|
||||
<div className="flex flex-row items-center gap-3 py-3">
|
||||
{/* <div className="flex flex-row items-center gap-3 py-3">
|
||||
<Label>{t("watermark")}</Label>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch defaultChecked color="primary" id="c2" />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<Button
|
||||
color="destructive"
|
||||
onClick={handleRemoveAllFiles}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,12 @@ export default function FormTeksUpdate() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"application/pdf": [],
|
||||
"application/msword": [], // .doc
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||
[], // .docx
|
||||
},
|
||||
});
|
||||
|
||||
const options: Option[] = [
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ import {
|
|||
rejectFiles,
|
||||
submitApproval,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import {
|
||||
detailMedia,
|
||||
getDataApprovalByMediaUpload,
|
||||
} from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { MailIcon } from "lucide-react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
|
@ -57,6 +60,9 @@ import { error } from "@/lib/swal";
|
|||
import dynamic from "next/dynamic";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { useTranslations } from "next-intl";
|
||||
import SuggestionModal from "@/components/modal/suggestions-modal";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -142,6 +148,7 @@ export default function FormVideoDetail() {
|
|||
const [files, setFiles] = useState<FileType[]>([]);
|
||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||
const [approval, setApproval] = useState<any>();
|
||||
|
||||
let fileTypeId = "2";
|
||||
|
||||
|
|
@ -252,6 +259,9 @@ export default function FormVideoDetail() {
|
|||
files.url ? files.url : "default-image.jpg"
|
||||
);
|
||||
setDetailVideo(fileUrls);
|
||||
|
||||
const approvals = await getDataApprovalByMediaUpload(details?.id);
|
||||
setApproval(approvals?.data?.data);
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -276,19 +286,20 @@ export default function FormVideoDetail() {
|
|||
Number(status) == 2 ||
|
||||
Number(status) == 4
|
||||
) {
|
||||
MySwal.fire({
|
||||
title: "Simpan Approval",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save();
|
||||
}
|
||||
});
|
||||
save();
|
||||
|
||||
// MySwal.fire({
|
||||
// title: "Simpan Approval",
|
||||
// text: "",
|
||||
// icon: "warning",
|
||||
// showCancelButton: true,
|
||||
// cancelButtonColor: "#d33",
|
||||
// confirmButtonColor: "#3085d6",
|
||||
// confirmButtonText: "Simpan",
|
||||
// }).then((result) => {
|
||||
// if (result.isConfirmed) {
|
||||
// }
|
||||
// });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -299,7 +310,7 @@ export default function FormVideoDetail() {
|
|||
message: description,
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
};
|
||||
|
||||
setModalOpen(false);
|
||||
loading();
|
||||
const response = await submitApproval(data);
|
||||
|
||||
|
|
@ -486,11 +497,11 @@ export default function FormVideoDetail() {
|
|||
navigation={false}
|
||||
className="w-full"
|
||||
>
|
||||
{detailVideo?.map((data: any) => (
|
||||
{files?.map((data: any) => (
|
||||
<SwiperSlide key={data.id}>
|
||||
<video
|
||||
className="object-fill h-full w-full"
|
||||
src={data}
|
||||
src={data.secondaryUrl}
|
||||
controls
|
||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||
/>
|
||||
|
|
@ -508,11 +519,11 @@ export default function FormVideoDetail() {
|
|||
modules={[Pagination, Thumbs]}
|
||||
// className="mySwiper2"
|
||||
>
|
||||
{detailVideo?.map((data: any) => (
|
||||
{files?.map((data: any) => (
|
||||
<SwiperSlide key={data.id}>
|
||||
<video
|
||||
className="object-cover h-[60px] w-[80px]"
|
||||
src={data}
|
||||
src={data.secondaryUrl}
|
||||
muted
|
||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||
/>
|
||||
|
|
@ -526,7 +537,7 @@ export default function FormVideoDetail() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-full lg:w-4/12">
|
||||
<Card className=" h-[800px]">
|
||||
<Card className="pb-3">
|
||||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("creator")}</Label>
|
||||
|
|
@ -614,13 +625,21 @@ export default function FormVideoDetail() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
|
||||
<MailIcon />
|
||||
<p className="">{t("suggestion-box")} (0)</p>
|
||||
</div>
|
||||
<SuggestionModal
|
||||
id={Number(id)}
|
||||
numberOfSuggestion={detail?.numberOfSuggestion}
|
||||
/>
|
||||
<div className="px-3 py-3 border mx-3">
|
||||
<p>{t("information")}:</p>
|
||||
<p className="text-sm text-slate-400">{detail?.statusName}</p>
|
||||
<p>Komentar</p>
|
||||
<p>{approval?.message}</p>
|
||||
<p className="text-right text-sm">
|
||||
{" "}
|
||||
{approval?.approvalBy?.fullname} |{" "}
|
||||
{formatDateToIndonesian(approval?.approvalDate)}
|
||||
</p>
|
||||
<ApprovalHistoryModal id={Number(id)} />
|
||||
</div>
|
||||
{/* {detail?.isPublish == false ? (
|
||||
<div className="p-3">
|
||||
|
|
@ -629,39 +648,6 @@ export default function FormVideoDetail() {
|
|||
) : (
|
||||
""
|
||||
)} */}
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
|
||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||
<DialogContent size="md">
|
||||
|
|
@ -674,7 +660,21 @@ export default function FormVideoDetail() {
|
|||
key={file.id}
|
||||
className="flex flex-row gap-2 items-center"
|
||||
>
|
||||
<img src={file.url} className="w-[200px]" />
|
||||
{/* <img src={file.url} className="w-[200px]" /> */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M20 3a2 2 0 0 1 1.995 1.85L22 5v14a2 2 0 0 1-1.85 1.995L20 21H4a2 2 0 0 1-1.995-1.85L2 19V5a2 2 0 0 1 1.85-1.995L4 3zm0 2H4v14h16zm-9.66 2.638l.518.23l.338.16l.387.19l.43.218l.47.25l.507.28l.266.152l.518.305l.474.292l.43.273l.38.253l.48.33l.364.263l.095.07a1.234 1.234 0 0 1 0 1.98l-.323.235l-.44.308l-.356.239l-.405.263l-.453.283l-.499.3l-.534.309l-.509.282l-.471.25l-.43.22l-.386.188l-.622.288l-.23.1a1.234 1.234 0 0 1-1.714-.99l-.058-.565l-.032-.374l-.042-.664l-.023-.508l-.015-.555l-.004-.294l-.002-.305q0-.31.006-.6l.015-.555l.023-.507l.027-.457l.03-.401l.075-.744a1.235 1.235 0 0 1 1.715-.992m.611 2.501l-.436-.218l-.029.487l-.022.551l-.013.61l-.002.325l.002.325l.013.609l.01.283l.026.52l.015.235l.434-.218l.487-.256l.535-.294l.284-.162l.551-.326l.494-.306l.436-.28l.196-.13l-.407-.27l-.466-.294a30 30 0 0 0-.803-.48l-.283-.161l-.534-.294z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between text-sm">
|
||||
{file.fileName}
|
||||
|
|
@ -861,6 +861,39 @@ export default function FormVideoDetail() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</Card>
|
||||
{Number(detail?.needApprovalFromLevel) == Number(userLevelId) ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" /> {t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" />{" "}
|
||||
{t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -148,6 +148,9 @@ export default function FormVideo() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"video/*": [],
|
||||
},
|
||||
});
|
||||
|
||||
const videoSchema = z.object({
|
||||
|
|
@ -666,7 +669,21 @@ export default function FormVideo() {
|
|||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="file-preview">{renderFilePreview(file)}</div>
|
||||
{/* <div className="file-preview">{renderFilePreview(file)}</div> */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M20 3a2 2 0 0 1 1.995 1.85L22 5v14a2 2 0 0 1-1.85 1.995L20 21H4a2 2 0 0 1-1.995-1.85L2 19V5a2 2 0 0 1 1.85-1.995L4 3zm0 2H4v14h16zm-9.66 2.638l.518.23l.338.16l.387.19l.43.218l.47.25l.507.28l.266.152l.518.305l.474.292l.43.273l.38.253l.48.33l.364.263l.095.07a1.234 1.234 0 0 1 0 1.98l-.323.235l-.44.308l-.356.239l-.405.263l-.453.283l-.499.3l-.534.309l-.509.282l-.471.25l-.43.22l-.386.188l-.622.288l-.23.1a1.234 1.234 0 0 1-1.714-.99l-.058-.565l-.032-.374l-.042-.664l-.023-.508l-.015-.555l-.004-.294l-.002-.305q0-.31.006-.6l.015-.555l.023-.507l.027-.457l.03-.401l.075-.744a1.235 1.235 0 0 1 1.715-.992m.611 2.501l-.436-.218l-.029.487l-.022.551l-.013.61l-.002.325l.002.325l.013.609l.01.283l.026.52l.015.235l.434-.218l.487-.256l.535-.294l.284-.162l.551-.326l.494-.306l.436-.28l.196-.13l-.407-.27l-.466-.294a30 30 0 0 0-.803-.48l-.283-.161l-.534-.294z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div>
|
||||
<div className=" text-sm text-card-foreground">{file.name}</div>
|
||||
<div className=" text-xs font-light text-muted-foreground">
|
||||
|
|
@ -1011,12 +1028,12 @@ export default function FormVideo() {
|
|||
<Fragment>
|
||||
<div>{fileList}</div>
|
||||
<div className=" flex justify-between gap-2">
|
||||
<div className="flex flex-row items-center gap-3 py-3">
|
||||
{/* <div className="flex flex-row items-center gap-3 py-3">
|
||||
<Label>{t("watermark")}</Label>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch defaultChecked color="primary" id="c2" />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<Button
|
||||
color="destructive"
|
||||
onClick={handleRemoveAllFiles}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,9 @@ export default function FormVideoUpdate() {
|
|||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"video/*": [],
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { CalendarIcon, Clock1, Locate, MapPin, User2 } from "lucide-react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { addDays, format, parseISO, setDate } from "date-fns";
|
||||
|
|
@ -32,6 +32,7 @@ import {
|
|||
detailSchedule,
|
||||
listScheduleNext,
|
||||
listScheduleToday,
|
||||
postApprovalSchedule,
|
||||
postSchedule,
|
||||
} from "@/service/schedule/schedule";
|
||||
import {
|
||||
|
|
@ -42,6 +43,18 @@ import {
|
|||
} from "@/components/ui/accordion";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
import { formatDate } from "@fullcalendar/core/index.js";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { close } from "@/config/swal";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -58,14 +71,22 @@ interface Detail {
|
|||
speakerName: string;
|
||||
addressLat: number;
|
||||
addressLong: number;
|
||||
isYoutube: boolean;
|
||||
youtubeUrl: string;
|
||||
needApprovalFrom: number;
|
||||
uploadedById: number;
|
||||
}
|
||||
|
||||
export default function FormDetailLiveReport() {
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
const router = useRouter();
|
||||
const [isLiveStreamingEnabled, setIsLiveStreamingEnabled] = useState(false);
|
||||
type TaskSchema = z.infer<typeof taskSchema>;
|
||||
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const userLevelNumber = getCookiesDecrypt("ulne");
|
||||
|
||||
const [startTime, setStartTime] = useState("08:00");
|
||||
const [endTime, setEndTime] = useState("09:00");
|
||||
const [date, setDate] = useState<DateRange | undefined>();
|
||||
|
|
@ -75,6 +96,12 @@ export default function FormDetailLiveReport() {
|
|||
const [detail, setDetail] = useState<Detail>();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
const t = useTranslations("Form");
|
||||
|
||||
const [status, setStatus] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
|
|
@ -82,9 +109,6 @@ export default function FormDetailLiveReport() {
|
|||
formState: { errors },
|
||||
} = useForm<TaskSchema>({
|
||||
resolver: zodResolver(taskSchema),
|
||||
defaultValues: {
|
||||
location: "",
|
||||
},
|
||||
});
|
||||
|
||||
async function getDataByDate() {
|
||||
|
|
@ -98,27 +122,30 @@ export default function FormDetailLiveReport() {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailSchedule(id);
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
if (details) {
|
||||
setDate({
|
||||
from: parseISO(details.startDate),
|
||||
to: parseISO(details.endDate),
|
||||
});
|
||||
}
|
||||
if (details) {
|
||||
setStartTime(details.startTime);
|
||||
setEndTime(details.endTime);
|
||||
}
|
||||
getDataByDate();
|
||||
}
|
||||
}
|
||||
initState();
|
||||
}, [refresh, setValue]);
|
||||
}, [refresh]);
|
||||
|
||||
async function initState() {
|
||||
if (id) {
|
||||
loading();
|
||||
const response = await detailSchedule(id);
|
||||
close();
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
if (details) {
|
||||
setDate({
|
||||
from: parseISO(details.startDate),
|
||||
to: parseISO(details.endDate),
|
||||
});
|
||||
}
|
||||
if (details) {
|
||||
setStartTime(details.startTime);
|
||||
setEndTime(details.endTime);
|
||||
}
|
||||
getDataByDate();
|
||||
}
|
||||
}
|
||||
|
||||
const handleStartTime = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setStartTime(e.target.value);
|
||||
|
|
@ -128,6 +155,75 @@ export default function FormDetailLiveReport() {
|
|||
setEndTime(e.target.value);
|
||||
};
|
||||
|
||||
const actionApproval = (e: string) => {
|
||||
setStatus(e);
|
||||
setDescription("");
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (
|
||||
description?.length > 1 &&
|
||||
(Number(status) == 3 || Number(status) == 2 || Number(status) == 4)
|
||||
) {
|
||||
save();
|
||||
}
|
||||
};
|
||||
|
||||
async function save() {
|
||||
const data = {
|
||||
scheduleId: Number(id),
|
||||
statusId: Number(status),
|
||||
message: description,
|
||||
isPublish: status === "2",
|
||||
placements: schedulePlacements?.filter((val) => val != "all")?.join(","),
|
||||
};
|
||||
|
||||
loading();
|
||||
const response = await postApprovalSchedule(data);
|
||||
close();
|
||||
setModalOpen(false);
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
initState();
|
||||
return false;
|
||||
}
|
||||
|
||||
const [schedulePlacements, setSchedulePlacements] = useState<string[]>([]);
|
||||
|
||||
const setupPlacement = (placement: string, checked: boolean) => {
|
||||
let temp = [...schedulePlacements];
|
||||
if (checked) {
|
||||
if (placement === "all") {
|
||||
temp = ["all", "mabes", "polda", "international"];
|
||||
} else {
|
||||
const now = temp;
|
||||
now.push(placement);
|
||||
if (now.length === 3 && !now.includes("all")) {
|
||||
now.push("all");
|
||||
}
|
||||
temp = now;
|
||||
}
|
||||
} else {
|
||||
if (placement === "all") {
|
||||
temp = [];
|
||||
} else {
|
||||
const now = temp.filter((a) => a !== placement);
|
||||
console.log("now", now);
|
||||
temp = now;
|
||||
if (now.length === 3 && now.includes("all")) {
|
||||
const newData = now.filter((b) => b !== "all");
|
||||
temp = newData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("temp");
|
||||
setSchedulePlacements(temp);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col lg:flex-row gap-2">
|
||||
<Card className="w-full lg:w-9/12">
|
||||
|
|
@ -157,39 +253,24 @@ export default function FormDetailLiveReport() {
|
|||
<div className="mt-6">
|
||||
<Label>Live Streaming</Label>
|
||||
<div className="flex items-center gap-3">
|
||||
<p>Aktifkan fitur live streaming</p>
|
||||
<p className="text-sm">Aktifkan fitur live streaming</p>
|
||||
<Switch
|
||||
defaultChecked={isLiveStreamingEnabled}
|
||||
checked={detail.isYoutube}
|
||||
color="primary"
|
||||
id="c2"
|
||||
onCheckedChange={(checked) =>
|
||||
setIsLiveStreamingEnabled(checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLiveStreamingEnabled && (
|
||||
{detail.isYoutube && (
|
||||
<div className="mt-1">
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan ID youtube"
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={detail.youtubeUrl}
|
||||
placeholder="Masukan ID youtube"
|
||||
/>
|
||||
{errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -332,12 +413,6 @@ export default function FormDetailLiveReport() {
|
|||
) : (
|
||||
""
|
||||
)}
|
||||
{/* Submit Button
|
||||
<div className="mt-4">
|
||||
<Button type="submit" color="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="w-full lg:w-3/12">
|
||||
|
|
@ -349,13 +424,31 @@ export default function FormDetailLiveReport() {
|
|||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{todayList?.length > 0 ? (
|
||||
<ul className="list-disc ml-4">
|
||||
<div className="list-disc ">
|
||||
{todayList.map((item: any, index) => (
|
||||
<li key={index} className="py-1">
|
||||
{item.name}
|
||||
</li>
|
||||
<div key={index} className="">
|
||||
<li className="text-base font-semibold">{item.title}</li>
|
||||
<p className="text-sm ml-5 flex my-2 gap-2">
|
||||
<CalendarIcon size={20} />
|
||||
{formatDate(item?.startDate)}-
|
||||
{formatDate(item?.endDate)}
|
||||
</p>
|
||||
<p className="text-sm ml-5 flex my-2 gap-2">
|
||||
<Clock1 size={20} />
|
||||
{item?.startTime}-{item?.endTime}
|
||||
</p>
|
||||
<p className="text-sm ml-5 flex items-center my-2 gap-2">
|
||||
<MapPin size={20} />
|
||||
{item?.address}
|
||||
</p>
|
||||
<p className="text-sm ml-5">Disampaikan oleh:</p>
|
||||
<p className="text-sm ml-5 flex my-2 items-center gap-2">
|
||||
<User2 size={20} />
|
||||
{item?.speakerTitle} {item?.speakerName}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-gray-500">Tidak ada jadwal hari ini</p>
|
||||
)}
|
||||
|
|
@ -373,26 +466,25 @@ export default function FormDetailLiveReport() {
|
|||
{nextDayList.map((item: any, index) => (
|
||||
<div key={index} className="">
|
||||
<li className="text-base font-semibold">{item.title}</li>
|
||||
<p className="text-sm ml-5 flex my-2">
|
||||
<p className="text-sm ml-5 flex my-2 gap-2">
|
||||
<CalendarIcon size={20} />
|
||||
{formatDate(item?.startDate)}-
|
||||
{formatDate(item?.endDate)}
|
||||
</p>
|
||||
<p className="text-sm ml-5 flex my-2">
|
||||
<p className="text-sm ml-5 flex my-2 gap-2">
|
||||
<Clock1 size={20} />
|
||||
{item?.startTime}-{item?.endTime}
|
||||
</p>
|
||||
<p className="text-sm ml-5 flex items-center my-2">
|
||||
<MapPin size={50} />
|
||||
<p className="text-sm ml-5 flex items-center my-2 gap-2">
|
||||
<MapPin size={20} />
|
||||
{item?.address}
|
||||
</p>
|
||||
<p className="text-sm ml-5">Disampaikan oleh:</p>
|
||||
<p className="text-sm ml-5 flex my-2">
|
||||
<User2 />
|
||||
<p className="text-sm ml-5 flex my-2 items-center gap-2">
|
||||
<User2 size={20} />
|
||||
{item?.speakerTitle} {item?.speakerName}
|
||||
</p>
|
||||
</div>
|
||||
// <p>{item.startDate}</p>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -401,7 +493,162 @@ export default function FormDetailLiveReport() {
|
|||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
{Number(detail?.needApprovalFrom) == Number(userLevelId) &&
|
||||
Number(userLevelNumber) < 2 ? (
|
||||
Number(detail?.uploadedById) == Number(userId) ? (
|
||||
""
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Button
|
||||
onClick={() => actionApproval("2")}
|
||||
color="primary"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:check" className="mr-3" />
|
||||
{t("accept")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("3")}
|
||||
className="bg-orange-400 hover:bg-orange-300"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:comment-o" className="mr-3" /> {t("revision")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => actionApproval("4")}
|
||||
color="destructive"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="fa:times" className="mr-3" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||
<DialogContent className="overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("leave-comment")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-1 text-sm">
|
||||
<p>
|
||||
{" "}
|
||||
Status:{" "}
|
||||
<span
|
||||
className={
|
||||
status === "2"
|
||||
? "text-primary"
|
||||
: status === "3"
|
||||
? "text-warning"
|
||||
: "text-destructive"
|
||||
}
|
||||
>
|
||||
{status === "2"
|
||||
? "Disetujui"
|
||||
: status === "3"
|
||||
? "Revisi"
|
||||
: "Ditolak"}
|
||||
</span>
|
||||
</p>
|
||||
{status === "2" && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
value="all"
|
||||
checked={schedulePlacements?.includes("all")}
|
||||
onCheckedChange={(e) => setupPlacement("all", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor="terms"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{t("all")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
checked={schedulePlacements?.includes("mabes")}
|
||||
onCheckedChange={(e) => setupPlacement("mabes", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor="terms"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Nasional
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
checked={schedulePlacements?.includes("polda")}
|
||||
onCheckedChange={(e) => setupPlacement("polda", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor="terms"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Wilayah
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
checked={schedulePlacements?.includes("international")}
|
||||
onCheckedChange={(e) =>
|
||||
setupPlacement("international", Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="terms"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Internasional
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<p>Deskripsi:</p>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Textarea
|
||||
placeholder="Type your message here."
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
onClick={() => submit()}
|
||||
disabled={
|
||||
description.length < 1 ||
|
||||
(schedulePlacements.length < 1 && status === "2")
|
||||
}
|
||||
>
|
||||
{t("submit")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="destructive"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,12 +39,14 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { toast } from "sonner";
|
||||
import { close, loading } from "@/config/swal";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
level: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
name: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
location: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
title: z.string().min(1, { message: "Judul harus diisi" }),
|
||||
level: z.string().min(1, { message: "Pangkat harus diisi" }),
|
||||
name: z.string().min(1, { message: "Nama harus diisi" }),
|
||||
location: z.string().min(1, { message: "Lokasi harus diisi" }),
|
||||
});
|
||||
|
||||
export default function FormLiveReport() {
|
||||
|
|
@ -56,8 +58,12 @@ export default function FormLiveReport() {
|
|||
const [startTime, setStartTime] = useState("08:00");
|
||||
const [endTime, setEndTime] = useState("09:00");
|
||||
const [scheduleTypeId, setScheduleTypeId] = React.useState<string>("1");
|
||||
const [youtubeUrl, setYoutubeUrl] = useState("");
|
||||
|
||||
const [date, setDate] = React.useState<DateRange | undefined>();
|
||||
const [date, setDate] = React.useState<DateRange | undefined>({
|
||||
from: new Date(),
|
||||
to: new Date(),
|
||||
});
|
||||
|
||||
const handleStartTime = (e: any) => {
|
||||
setStartTime(e.target.value);
|
||||
|
|
@ -104,12 +110,14 @@ export default function FormLiveReport() {
|
|||
startDate: date?.from ? format(date.from, "yyyy-MM-dd") : null,
|
||||
endDate: date?.to ? format(date.to, "yyyy-MM-dd") : null,
|
||||
isYoutube: isLiveStreamingEnabled,
|
||||
youtubeUrl: isLiveStreamingEnabled ? youtubeUrl : null,
|
||||
scheduleTypeId: Number(scheduleTypeId),
|
||||
};
|
||||
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
loading();
|
||||
const response = await postSchedule(requestData);
|
||||
close();
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
|
|
@ -132,6 +140,11 @@ export default function FormLiveReport() {
|
|||
};
|
||||
|
||||
const onSubmit = (data: TaskSchema) => {
|
||||
if (isLiveStreamingEnabled && youtubeUrl == "") {
|
||||
toast(<p className="text-red-600">Youtube ID harus diisi</p>);
|
||||
return false;
|
||||
}
|
||||
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
|
|
@ -193,24 +206,13 @@ export default function FormLiveReport() {
|
|||
|
||||
{isLiveStreamingEnabled && (
|
||||
<div className="mb-2 mt-1">
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan ID youtube"
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={youtubeUrl}
|
||||
onChange={(e) => setYoutubeUrl(e.target.value)}
|
||||
placeholder="Masukan Youtube ID"
|
||||
/>
|
||||
{errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col space-y-2">
|
||||
|
|
@ -312,9 +314,11 @@ export default function FormLiveReport() {
|
|||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="invalid-feedback">
|
||||
{errors.location?.message}
|
||||
</div>
|
||||
{errors.location?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.location.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm mt-4 font-semibold">DI SAMPAIKAN OLEH</p>
|
||||
<div className="flex flex-col ">
|
||||
|
|
@ -374,9 +378,9 @@ export default function FormLiveReport() {
|
|||
</form>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="w-full lg:w-3/12">
|
||||
{/* <Card className="w-full lg:w-3/12">
|
||||
<div className="px-3 py-3">Jadwal Selanjutnya</div>
|
||||
</Card>
|
||||
</Card> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ 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 { useParams, useRouter, useSearchParams } from "next/navigation";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -29,12 +29,13 @@ import { Textarea } from "@/components/ui/textarea";
|
|||
import { error, loading } from "@/lib/swal";
|
||||
import Cookies from "js-cookie";
|
||||
import { detailSchedule, postSchedule } from "@/service/schedule/schedule";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
level: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
name: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
location: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
title: z.string().min(1, { message: "Judul harus diisi" }),
|
||||
level: z.string().min(1, { message: "Pangkat harus diisi" }),
|
||||
name: z.string().min(1, { message: "Nama harus diisi" }),
|
||||
location: z.string().min(1, { message: "Lokasi harus diisi" }),
|
||||
});
|
||||
|
||||
interface Detail {
|
||||
|
|
@ -49,8 +50,9 @@ interface Detail {
|
|||
|
||||
export default function FormUpdateLiveReport() {
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
const router = useRouter();
|
||||
const params = useSearchParams();
|
||||
const scheduleTypeId = params?.get("scheduleType");
|
||||
const MySwal = withReactContent(Swal);
|
||||
const [isLiveStreamingEnabled, setIsLiveStreamingEnabled] = useState(false);
|
||||
type TaskSchema = z.infer<typeof taskSchema>;
|
||||
|
|
@ -60,7 +62,7 @@ export default function FormUpdateLiveReport() {
|
|||
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [location, setLocation] = useState("");
|
||||
const [youtubeUrl, setYoutubeUrl] = useState("");
|
||||
|
||||
const {
|
||||
control,
|
||||
|
|
@ -69,28 +71,28 @@ export default function FormUpdateLiveReport() {
|
|||
formState: { errors },
|
||||
} = useForm<TaskSchema>({
|
||||
resolver: zodResolver(taskSchema),
|
||||
defaultValues: {
|
||||
location: "",
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailSchedule(id);
|
||||
const details = response?.data?.data;
|
||||
const data = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
if (details) {
|
||||
if (data) {
|
||||
setDetail(data);
|
||||
setValue("title", data.title);
|
||||
setValue("level", data.speakerTitle);
|
||||
setValue("name", data.speakerName);
|
||||
setValue("location", data.address);
|
||||
setDate({
|
||||
from: parseISO(details.startDate),
|
||||
to: parseISO(details.endDate),
|
||||
from: parseISO(data.startDate),
|
||||
to: parseISO(data.endDate),
|
||||
});
|
||||
}
|
||||
if (details) {
|
||||
setStartTime(details.startTime);
|
||||
setEndTime(details.endTime);
|
||||
setLocation(details.address);
|
||||
setStartTime(data.startTime);
|
||||
setYoutubeUrl(data.youtubeUrl);
|
||||
setEndTime(data.endTime);
|
||||
setIsLiveStreamingEnabled(data.isYoutube);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -107,7 +109,6 @@ export default function FormUpdateLiveReport() {
|
|||
|
||||
const handleLocationChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
setLocation(value);
|
||||
setValue("location", value);
|
||||
};
|
||||
|
||||
|
|
@ -126,6 +127,7 @@ export default function FormUpdateLiveReport() {
|
|||
endDate: string | null;
|
||||
isYoutube: boolean;
|
||||
scheduleTypeId: number;
|
||||
youtubeUrl: string;
|
||||
} = {
|
||||
title: data.title,
|
||||
address: data.location,
|
||||
|
|
@ -138,7 +140,8 @@ export default function FormUpdateLiveReport() {
|
|||
startDate: date?.from ? format(date.from, "yyyy-MM-dd") : null,
|
||||
endDate: date?.to ? format(date.to, "yyyy-MM-dd") : null,
|
||||
isYoutube: isLiveStreamingEnabled,
|
||||
scheduleTypeId: 1,
|
||||
scheduleTypeId: Number(scheduleTypeId),
|
||||
youtubeUrl: youtubeUrl,
|
||||
};
|
||||
|
||||
if (id) {
|
||||
|
|
@ -165,11 +168,16 @@ export default function FormUpdateLiveReport() {
|
|||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/schedule/press-conference");
|
||||
router.push("/en/contributor/schedule/live-report");
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = (data: TaskSchema) => {
|
||||
if (isLiveStreamingEnabled && youtubeUrl == "") {
|
||||
toast(<p className="text-red-600">Youtube ID harus diisi</p>);
|
||||
return false;
|
||||
}
|
||||
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
|
|
@ -191,7 +199,7 @@ export default function FormUpdateLiveReport() {
|
|||
<div className="px-6 py-6">
|
||||
<p className="text-lg font-semibold mb-3">Form Konferensi Pers</p>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{detail !== undefined ? (
|
||||
{detail ? (
|
||||
<div className=" gap-5 mb-5">
|
||||
{/* Input Title */}
|
||||
<div className="space-y-2">
|
||||
|
|
@ -202,7 +210,7 @@ export default function FormUpdateLiveReport() {
|
|||
render={({ field }) => (
|
||||
<Input
|
||||
type="text"
|
||||
defaultValue={detail?.title}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -219,7 +227,7 @@ export default function FormUpdateLiveReport() {
|
|||
<div className="flex items-center gap-3">
|
||||
<p>Aktifkan fitur live streaming</p>
|
||||
<Switch
|
||||
defaultChecked={isLiveStreamingEnabled}
|
||||
checked={isLiveStreamingEnabled}
|
||||
color="primary"
|
||||
id="c2"
|
||||
onCheckedChange={(checked) =>
|
||||
|
|
@ -231,25 +239,14 @@ export default function FormUpdateLiveReport() {
|
|||
</div>
|
||||
|
||||
{isLiveStreamingEnabled && (
|
||||
<div className="mt-1">
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan ID youtube"
|
||||
/>
|
||||
)}
|
||||
<div className="mb-2 mt-1">
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={youtubeUrl}
|
||||
onChange={(e) => setYoutubeUrl(e.target.value)}
|
||||
placeholder="Masukan Youtube ID"
|
||||
/>
|
||||
{errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -319,7 +316,6 @@ export default function FormUpdateLiveReport() {
|
|||
<MapHome
|
||||
draggable
|
||||
setLocation={(location) => {
|
||||
setLocation(location);
|
||||
setValue("location", location);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -331,8 +327,8 @@ export default function FormUpdateLiveReport() {
|
|||
render={({ field }) => (
|
||||
<Textarea
|
||||
rows={3}
|
||||
value={location}
|
||||
onChange={handleLocationChange}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan lokasi"
|
||||
/>
|
||||
)}
|
||||
|
|
@ -352,7 +348,7 @@ export default function FormUpdateLiveReport() {
|
|||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
defaultValue={detail?.speakerTitle}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan Nama Pangkat"
|
||||
/>
|
||||
|
|
@ -375,7 +371,7 @@ export default function FormUpdateLiveReport() {
|
|||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
defaultValue={detail?.speakerName}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan Nama Lengkap"
|
||||
/>
|
||||
|
|
@ -401,9 +397,9 @@ export default function FormUpdateLiveReport() {
|
|||
</form>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="w-full lg:w-3/12">
|
||||
{/* <Card className="w-full lg:w-3/12">
|
||||
<div className="px-3 py-3">Jadwal Selanjutnya</div>
|
||||
</Card>
|
||||
</Card> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,13 +40,11 @@ import { Icon } from "@/components/ui/icon";
|
|||
const calendarSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
redirectLink: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
type FileWithPreview = {
|
||||
file: File;
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface FileUploaded {
|
||||
id: number;
|
||||
|
|
@ -70,7 +68,9 @@ export function TambahIklanModal() {
|
|||
FileUploaded[]
|
||||
>([]);
|
||||
const [imageFiles, setImageFiles] = React.useState<FileWithPreview[]>([]);
|
||||
const [date, setDate] = React.useState<DateRange | undefined>();
|
||||
const [date, setDate] = React.useState<DateRange | undefined>({
|
||||
from: new Date(2025, 0, 1),
|
||||
});
|
||||
|
||||
const [unitSelection, setUnitSelection] = React.useState({
|
||||
semua: false,
|
||||
|
|
@ -181,46 +181,107 @@ export function TambahIklanModal() {
|
|||
satker: "4",
|
||||
internasional: "5",
|
||||
};
|
||||
|
||||
const assignmentToString = Object.keys(unitSelection)
|
||||
.filter((key) => unitSelection[key as keyof typeof unitSelection])
|
||||
.map((key) => unitMapping[key as keyof typeof unitMapping])
|
||||
.join(",");
|
||||
|
||||
const assignedLevel = handlePoldaPolresChange();
|
||||
const formMedia = new FormData();
|
||||
formMedia.append("title", data.title);
|
||||
formMedia.append("placements", selectedPlacement);
|
||||
formMedia.append("description", data.description);
|
||||
formMedia.append("redirectLink", "https://new.netidhub.com");
|
||||
formMedia.append("assignedToLevel", handlePoldaPolresChange());
|
||||
formMedia.append("file", imageFiles[0]);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("title", data.title);
|
||||
formData.append("placements", selectedPlacement);
|
||||
formData.append("description", data.description);
|
||||
formData.append("redirectLink", data.redirectLink);
|
||||
formData.append("createdBy", assignmentToString);
|
||||
formData.append("assignedToLevel", assignedLevel);
|
||||
console.log("Form Data Submitted:", formMedia);
|
||||
|
||||
// Append each actual File instance
|
||||
if (imageFiles.length > 0) {
|
||||
formData.append("files", imageFiles[0].file);
|
||||
loading();
|
||||
const response = await postAdvertisements(formMedia);
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
|
||||
try {
|
||||
const response = await postAdvertisements(formData);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
Cookies.set("scheduleId", response?.data?.data.id, {
|
||||
expires: 1,
|
||||
});
|
||||
|
||||
loading();
|
||||
setIsImageUploadFinish(true);
|
||||
} catch (e: any) {
|
||||
error(e.message || "Terjadi kesalahan saat menyimpan data");
|
||||
}
|
||||
Cookies.set("scheduleId", response?.data?.data.id, {
|
||||
expires: 1,
|
||||
});
|
||||
};
|
||||
|
||||
async function uploadResumableFile(
|
||||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
fileTypeId: string,
|
||||
duration: string
|
||||
) {
|
||||
console.log(idx, id, file, fileTypeId, duration);
|
||||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/advertisements/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
metadata: {
|
||||
advertisementsId: id,
|
||||
filename: file.name,
|
||||
contentType: file.type,
|
||||
fileTypeId: fileTypeId,
|
||||
duration,
|
||||
},
|
||||
onBeforeRequest: function (req) {
|
||||
var xhr = req.getUnderlyingObject();
|
||||
xhr.withCredentials = true;
|
||||
},
|
||||
onError: async (e: any) => {
|
||||
console.log("Error upload :", e);
|
||||
error(e);
|
||||
},
|
||||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
) => {
|
||||
// const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
// progressInfo[idx].percentage = uploadPersen;
|
||||
// counterUpdateProgress++;
|
||||
// console.log(counterUpdateProgress);
|
||||
// setProgressList(progressInfo);
|
||||
// setCounterProgress(counterUpdateProgress);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
// uploadPersen = 100;
|
||||
// progressInfo[idx].percentage = 100;
|
||||
// counterUpdateProgress++;
|
||||
// setCounterProgress(counterUpdateProgress);
|
||||
successTodo();
|
||||
if (fileTypeId == "1") {
|
||||
setIsImageUploadFinish(true);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
successTodo();
|
||||
}, [isImageUploadFinish]);
|
||||
|
||||
function successTodo() {
|
||||
if (isImageUploadFinish) {
|
||||
successSubmit("/in/admin/settings/iklan");
|
||||
}
|
||||
}
|
||||
|
||||
const successSubmit = (redirect: string) => {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
|
|
@ -269,7 +330,7 @@ export function TambahIklanModal() {
|
|||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="font-medium">Target Area</p>
|
||||
<div className="flex flex-wrap gap-4 mt-2 ml-3">
|
||||
<div className="flex flex-wrap gap-4 mt-2">
|
||||
{[
|
||||
{ label: "Kiri - 1", value: "left-top" },
|
||||
{ label: "Kiri - 2", value: "left-bottom" },
|
||||
|
|
@ -417,50 +478,55 @@ export function TambahIklanModal() {
|
|||
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">Link</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="redirectLink"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size={"md"}
|
||||
type="text"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Masukan Link"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-medium">Foto</p>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
multiple
|
||||
onChange={(e) => {
|
||||
if (e.target.files) {
|
||||
const files = Array.from(e.target.files).map((file) => ({
|
||||
file: file,
|
||||
preview: URL.createObjectURL(file),
|
||||
}));
|
||||
setImageFiles(files); // ✅ simpan objek yang punya properti .file dan .preview
|
||||
}
|
||||
<Label>Foto</Label>
|
||||
<p className="text-xs text-red-500">
|
||||
(Warning: Foto yang di upload adalah Foto Potrait)
|
||||
</p>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"image/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .png, .jpg, atau .jpeg."
|
||||
onDrop={(files) => setImageFiles(files)}
|
||||
/>
|
||||
{imageFiles.map(({ preview }, index) => (
|
||||
<Card key={index} className="mt-2">
|
||||
<img
|
||||
src={preview}
|
||||
alt={`Preview ${index}`}
|
||||
className="w-full h-auto rounded-md"
|
||||
/>
|
||||
</Card>
|
||||
{imageUploadedFiles?.map((file: any, index: number) => (
|
||||
<div>
|
||||
<Card className="mt-2">
|
||||
<img
|
||||
src={file.url}
|
||||
alt="Thumbnail Gambar Utama"
|
||||
className="w-full h-auto rounded-md"
|
||||
/>
|
||||
</Card>
|
||||
<div
|
||||
key={index}
|
||||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="file-preview">
|
||||
{renderFilePreview(file.url)}
|
||||
</div>
|
||||
<div>
|
||||
<div className=" text-sm text-card-foreground">
|
||||
{file.fileName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="icon"
|
||||
color="destructive"
|
||||
variant="outline"
|
||||
className=" border-none rounded-full"
|
||||
onClick={() => handleRemoveFile(file)}
|
||||
>
|
||||
<Icon icon="tabler:x" className=" h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -907,7 +907,7 @@ export default function FormTaskTaDetail() {
|
|||
<div className="px-6 py-6">
|
||||
<div className="flex flex-col sm:flex-row lg:flex-row justify-between">
|
||||
<p className="text-lg font-semibold mb-3">{t("detail-task")}</p>
|
||||
<div
|
||||
{/* <div
|
||||
className="flex gap-3"
|
||||
style={
|
||||
detail?.createdBy?.id === Number(userId)
|
||||
|
|
@ -950,7 +950,7 @@ export default function FormTaskTaDetail() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<form>
|
||||
|
|
@ -1320,7 +1320,7 @@ export default function FormTaskTaDetail() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap lg:flex-row justify-between gap-3 my-3">
|
||||
<div className="lg:px-1">
|
||||
{/* <div className="lg:px-1">
|
||||
{detail?.isDone !== true &&
|
||||
(Number(userLevelNumber) !== 3 ||
|
||||
Number(userLevelNumber) == 2) ? (
|
||||
|
|
@ -1336,9 +1336,9 @@ export default function FormTaskTaDetail() {
|
|||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="">
|
||||
{/* <div className="">
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
|
|
@ -1347,8 +1347,8 @@ export default function FormTaskTaDetail() {
|
|||
>
|
||||
Beri Tanggapan
|
||||
</Button>
|
||||
</div>
|
||||
<div className="">
|
||||
</div> */}
|
||||
{/* <div className="">
|
||||
<Button
|
||||
type="button"
|
||||
className="btn btn-primary lg:mx-3"
|
||||
|
|
@ -1364,9 +1364,9 @@ export default function FormTaskTaDetail() {
|
|||
>
|
||||
Terima Tugas
|
||||
</Button>
|
||||
</div>
|
||||
</div> */}
|
||||
<div
|
||||
className="task-response w-100 lg:px-3 "
|
||||
className="task-response w-100 lg:px-3"
|
||||
// style={
|
||||
// Number(detail?.createdBy?.id) == Number(userId)
|
||||
// ? {}
|
||||
|
|
@ -1384,7 +1384,7 @@ export default function FormTaskTaDetail() {
|
|||
if (!isTableResult) fetchAllData(); // Panggil API saat tombol diklik
|
||||
}}
|
||||
>
|
||||
Hasil Upload {Number(userId)}
|
||||
Hasil Upload
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1406,7 +1406,7 @@ export default function FormTaskTaDetail() {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<Dialog>
|
||||
{/* <Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" size="md" color="primary">
|
||||
Filter Polda/Polres
|
||||
|
|
@ -1486,7 +1486,7 @@ export default function FormTaskTaDetail() {
|
|||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Dialog> */}
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full border-collapse border border-gray-300">
|
||||
|
|
|
|||
|
|
@ -156,7 +156,9 @@ export default function FormTaskTa() {
|
|||
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
|
||||
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
|
||||
const [voiceNoteLink, setVoiceNoteLink] = useState("");
|
||||
const [date, setDate] = React.useState<DateRange | undefined>();
|
||||
const [date, setDate] = React.useState<DateRange | undefined>({
|
||||
from: new Date(),
|
||||
});
|
||||
|
||||
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
|
|
@ -398,7 +400,7 @@ export default function FormTaskTa() {
|
|||
fileTypeId: string,
|
||||
duration: string
|
||||
) {
|
||||
console.log(idx, id, file, fileTypeId, duration);
|
||||
console.log("Tus Upload : ", idx, id, file, fileTypeId, duration);
|
||||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
|
|
@ -629,7 +631,10 @@ export default function FormTaskTa() {
|
|||
}
|
||||
className="mr-3"
|
||||
/>
|
||||
{expert.fullname}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-bold">{expert.fullname}</div>
|
||||
<div className="italic">({expert.username})</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -637,6 +642,46 @@ export default function FormTaskTa() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
{checkedLevels.size > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-sm text-gray-600 mb-2 block">
|
||||
Tenaga Ahli Terpilih ({checkedLevels.size})
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(checkedLevels).map((expertId) => {
|
||||
const expert = listExpert?.find(
|
||||
(exp: any) => exp.id === expertId
|
||||
);
|
||||
return expert ? (
|
||||
<div
|
||||
key={expert.id}
|
||||
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<span>{expert.fullname}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxChange(expert.id)}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
|
||||
title="Remove expert"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
|
|
|
|||
|
|
@ -31,34 +31,25 @@ interface Advertisement {
|
|||
// };
|
||||
// }
|
||||
|
||||
const LeftBanner = () => {
|
||||
const AdvertisementPlacements = (props: { placement?: string }) => {
|
||||
const [ads, setAds] = useState<Advertisement[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
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 [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||
const [page, setPage] = React.useState(1);
|
||||
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const res = await listDataAdvertisements( page - 1,
|
||||
showData,
|
||||
"",
|
||||
categoryFilter?.sort().join(","),
|
||||
statusFilter?.sort().join(","));
|
||||
const res = await listDataAdvertisements(
|
||||
0,
|
||||
"4",
|
||||
""
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
|
||||
contentData.forEach((item: Advertisement, index: number) => {
|
||||
item.no = index + 1;
|
||||
});
|
||||
|
||||
setAds(contentData);
|
||||
if (props.placement == "left") {
|
||||
setAds(contentData.slice(0,2));
|
||||
} else {
|
||||
setAds(contentData.slice(2));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching advertisements:", error);
|
||||
} finally {
|
||||
|
|
@ -71,13 +62,13 @@ const LeftBanner = () => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 space-y-4 ml-14">
|
||||
<div className={`sticky top-0 space-y-4 ${props.placement == "left" ? "ml-14" : "mr-14"}`}>
|
||||
{loading && <p className="text-sm text-gray-500">Loading...</p>}
|
||||
{ads.map((ad) => (
|
||||
<img key={ad.id} src={ad.imageUrl} alt={`Banner ${ad.id}`} width={180} />
|
||||
{ads?.map((ad) => (
|
||||
<img key={ad.id} src={ad.contentFileUrl} alt={`Banner ${ad.id}`} className="w-full" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LeftBanner;
|
||||
export default AdvertisementPlacements;
|
||||
|
|
@ -1,86 +1,285 @@
|
|||
import React from "react";
|
||||
import { getCalendarPagination } from "@/service/schedule/schedule";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
interface CalendarItem {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
assignedTo: string;
|
||||
assignedToLevel: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
isActive: boolean;
|
||||
createdById: number;
|
||||
createdByName: string;
|
||||
thumbnailUrl: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
const EventCalender = () => {
|
||||
// Get current date
|
||||
const today = new Date();
|
||||
const currentMonth = today.getMonth();
|
||||
const currentYear = today.getFullYear();
|
||||
const currentDate = today.getDate();
|
||||
|
||||
const [events, setEvents] = useState<CalendarItem[]>([]);
|
||||
const [selectedEvent, setSelectedEvent] = useState<CalendarItem | null>(null);
|
||||
|
||||
// Month names in Indonesian
|
||||
const monthNames = [
|
||||
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
|
||||
"Juli", "Agustus", "September", "Oktober", "November", "Desember"
|
||||
];
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await getCalendarPagination(100, 0);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
setEvents(contentData || []);
|
||||
|
||||
// Set first event as selected by default
|
||||
if (contentData && contentData.length > 0) {
|
||||
setSelectedEvent(contentData[0]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching calendar events:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// Get first day of the month and number of days
|
||||
const firstDayOfMonth = new Date(currentYear, currentMonth, 1);
|
||||
const lastDayOfMonth = new Date(currentYear, currentMonth + 1, 0);
|
||||
const daysInMonth = lastDayOfMonth.getDate();
|
||||
const startingDayOfWeek = firstDayOfMonth.getDay();
|
||||
|
||||
// Convert Sunday (0) to 7 for Monday-first week
|
||||
const adjustedStartingDay = startingDayOfWeek === 0 ? 6 : startingDayOfWeek - 1;
|
||||
|
||||
// Generate calendar days
|
||||
const generateCalendarDays = () => {
|
||||
const days = [];
|
||||
|
||||
// Empty cells for days before the first day of month
|
||||
for (let i = 0; i < adjustedStartingDay; i++) {
|
||||
days.push(null);
|
||||
}
|
||||
|
||||
// Days of the month
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
days.push(day);
|
||||
}
|
||||
|
||||
// Fill remaining cells to complete the grid (6 rows × 7 days = 42 cells)
|
||||
while (days.length < 42) {
|
||||
days.push(null);
|
||||
}
|
||||
|
||||
return days;
|
||||
};
|
||||
|
||||
const calendarDays = generateCalendarDays();
|
||||
|
||||
// Helper function to extract day from date string
|
||||
const getDateFromString = (dateString: string) => {
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
if (date.getMonth() === currentMonth && date.getFullYear() === currentYear) {
|
||||
return date.getDate();
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to format date range
|
||||
const formatDateRange = (startDate: string, endDate: string) => {
|
||||
try {
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
const startDay = start.getDate();
|
||||
const endDay = end.getDate();
|
||||
const startMonth = monthNames[start.getMonth()];
|
||||
const endMonth = monthNames[end.getMonth()];
|
||||
|
||||
if (startDay === endDay && startMonth === endMonth) {
|
||||
return `${startDay} ${startMonth}`;
|
||||
} else {
|
||||
return `${startDay} ${startMonth} - ${endDay} ${endMonth}`;
|
||||
}
|
||||
} catch {
|
||||
return "Tanggal tidak valid";
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to format time range
|
||||
const formatTimeRange = (startDate: string, endDate: string) => {
|
||||
try {
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
const startTime = start.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Asia/Jakarta'
|
||||
});
|
||||
const endTime = end.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Asia/Jakarta'
|
||||
});
|
||||
|
||||
return `${startTime} - ${endTime} WIB`;
|
||||
} catch {
|
||||
return "Waktu tidak tersedia";
|
||||
}
|
||||
};
|
||||
|
||||
// Get event dates for highlighting calendar
|
||||
const eventDates = events
|
||||
.map(event => getDateFromString(event.startDate))
|
||||
.filter(date => date !== null);
|
||||
|
||||
return (
|
||||
<div className="mt-8 rounded-lg bg-white dark:bg-zinc-900 p-4 shadow">
|
||||
<h2 className="text-lg font-bold text-red-600 border-b border-red-600 mb-4 pb-2">
|
||||
KALENDER ACARA
|
||||
</h2>
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="w-full md:w-1/2">
|
||||
<div className="bg-gray-100 dark:bg-zinc-800 p-4 rounded-md ">
|
||||
<div className="text-center font-semibold mb-2">Mei 2025</div>
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
{/* Left Side - Calendar and Event List */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
{/* Mini Calendar */}
|
||||
<div className="bg-gray-100 dark:bg-zinc-800 p-4 rounded-md mb-4">
|
||||
<div className="text-center font-semibold mb-2">
|
||||
{monthNames[currentMonth]} {currentYear}
|
||||
</div>
|
||||
<div className="grid grid-cols-7 gap-1 text-sm text-center">
|
||||
{["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => (
|
||||
<div key={d} className="font-medium">
|
||||
<div key={d} className="font-medium p-1">
|
||||
{d}
|
||||
</div>
|
||||
))}
|
||||
{[...Array(35)].map((_, i) => (
|
||||
{calendarDays?.map((day, index) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`p-1 rounded ${
|
||||
[6, 7, 15].includes(i) ? "bg-red-600 text-white" : ""
|
||||
key={index}
|
||||
className={`p-1 rounded min-h-[24px] flex items-center justify-center text-xs ${
|
||||
day === null
|
||||
? ""
|
||||
: eventDates.includes(day)
|
||||
? "bg-red-600 text-white font-semibold"
|
||||
: day === currentDate
|
||||
? "bg-blue-500 text-white font-semibold"
|
||||
: "hover:bg-gray-200 dark:hover:bg-zinc-700"
|
||||
}`}
|
||||
>
|
||||
{i >= 2 && i - 1}
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4 mt-3">
|
||||
<div className="flex items-center bg-gray-200 rounded-xl shadow-sm p-2">
|
||||
<img
|
||||
src="/images/all-img/calendar1.png"
|
||||
alt="HUT Polwan"
|
||||
className="w-24 h-20 object-cover rounded"
|
||||
/>
|
||||
<div className="flex justify-between items-center w-full ml-3">
|
||||
<div className="text-sm font-semibold line-clamp-2">
|
||||
HUT Polwan ke-76, Kapolri...
|
||||
</div>
|
||||
<div className="text-sm font-semibold whitespace-nowrap ml-3">
|
||||
16 Mei - 16 Mei
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center bg-gray-200 rounded-xl shadow-sm p-2">
|
||||
<img
|
||||
src="/images/all-img/calendar2.png"
|
||||
alt="Olahraga"
|
||||
className="w-24 h-20 object-cover rounded"
|
||||
/>
|
||||
<div className="flex justify-between items-center w-full ml-3">
|
||||
<div className="text-sm font-semibold line-clamp-2 uppercase">
|
||||
Olahraga Bersama Pad...
|
||||
</div>
|
||||
<div className="text-sm font-semibold whitespace-nowrap ml-3">
|
||||
22 Mei - 22 Mei
|
||||
</div>
|
||||
{/* Event List */}
|
||||
<div className="space-y-3 max-h-[230px] overflow-y-auto pr-5">
|
||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-3">
|
||||
Daftar Acara
|
||||
</h3>
|
||||
{events?.length === 0 ? (
|
||||
<div className="text-center text-gray-500 dark:text-gray-400 py-8">
|
||||
Tidak ada acara yang tersedia
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
events.map((event) => (
|
||||
<div
|
||||
key={event.id}
|
||||
onClick={() => setSelectedEvent(event)}
|
||||
className={`flex items-center rounded-xl shadow-sm p-3 cursor-pointer transition-all duration-200 hover:shadow-md ${
|
||||
selectedEvent?.id === event.id
|
||||
? "bg-red-100 dark:bg-red-900/20 border-2 border-red-500"
|
||||
: "bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700"
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={event.thumbnailUrl || "/images/default-event.png"}
|
||||
alt={event.title}
|
||||
className="w-16 h-12 object-cover rounded flex-shrink-0"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = "/images/default-event.png";
|
||||
}}
|
||||
/>
|
||||
<div className="ml-3 flex-1 min-w-0">
|
||||
<div className="text-sm font-semibold text-gray-800 dark:text-gray-200 line-clamp-2">
|
||||
{event.title}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
||||
{formatDateRange(event.startDate, event.endDate)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-2 flex-shrink-0">
|
||||
<div className={`w-2 h-2 rounded-full ${event.isActive ? 'bg-green-500' : 'bg-red-500'}`}></div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Detail Acara */}
|
||||
<div className="w-full md:w-1/2 space-y-4">
|
||||
<div className="bg-gray-100 dark:bg-zinc-800 rounded-md p-2 mt-4">
|
||||
<img
|
||||
src="/images/all-img/calendar1.png"
|
||||
alt="Detail Event"
|
||||
className="rounded mb-2"
|
||||
/>
|
||||
<p className="text-sm font-semibold mb-1">
|
||||
HUT Polwan ke-76, Kapolri Apresiasi Prestasi yang Diberikan
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mb-1">
|
||||
Kapolri Jenderal Pol. Listyo Sigit Prabowo memberikan apresiasi
|
||||
kepada polisi wanita yang berprestasi...
|
||||
</p>
|
||||
<a href="#" className="text-xs text-blue-500">
|
||||
Lihat Selengkapnya
|
||||
</a>
|
||||
{/* Right Side - Event Detail */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className="bg-gray-100 dark:bg-zinc-800 rounded-lg p-4 sticky top-4">
|
||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">
|
||||
Detail Acara
|
||||
</h3>
|
||||
|
||||
{selectedEvent ? (
|
||||
<div className="space-y-4">
|
||||
<img
|
||||
src={selectedEvent.thumbnailUrl || "/images/default-event.png"}
|
||||
alt={selectedEvent.title}
|
||||
className="w-full h-48 object-cover rounded-lg"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = "/images/default-event.png";
|
||||
}}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<h4 className="text-lg font-bold text-gray-800 dark:text-gray-200 mb-2">
|
||||
{selectedEvent.title}
|
||||
</h4>
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 mb-4">
|
||||
<div className="flex items-start text-sm text-gray-600 dark:text-gray-400">
|
||||
<span className="w-20 font-semibold flex-shrink-0">Tanggal:</span>
|
||||
<span>{formatDateRange(selectedEvent.startDate, selectedEvent.endDate)}</span>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
|
||||
{selectedEvent.description || "Tidak ada deskripsi tersedia."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button className="px-4 py-2 bg-gray-300 dark:bg-zinc-700 text-gray-700 dark:text-gray-300 text-sm font-medium rounded-lg hover:bg-gray-400 dark:hover:bg-zinc-600 transition-colors">
|
||||
Bagikan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center text-gray-500 dark:text-gray-400 py-8">
|
||||
Pilih acara untuk melihat detail
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -88,4 +287,4 @@ const EventCalender = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default EventCalender;
|
||||
export default EventCalender;
|
||||
|
|
@ -12,20 +12,7 @@ import AreaCoverageWorkUnits from "./area-coverage-and-work-units";
|
|||
import EventCalender from "./event-calender";
|
||||
import UserSurveyBox from "./survey-box";
|
||||
import ScrollableContentPolda from "./scrollable-content-polda";
|
||||
|
||||
const LeftBanner = () => (
|
||||
<div className="sticky top-0 space-y-4">
|
||||
<img src="/images/all-img/kiri1.png" alt="Banner Kiri 1" />
|
||||
<img src="/images/all-img/kiri2.png" alt="Banner Kiri 2" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const RightBanner = () => (
|
||||
<div className="sticky top-0 space-y-4">
|
||||
<img src="/images/all-img/kanan2.png" alt="Banner Kanan 1" />
|
||||
<img src="/images/all-img/kanan1.png" alt="Banner Kanan 2" />
|
||||
</div>
|
||||
);
|
||||
import AdvertisementPlacements from "./advertisement-placements";
|
||||
|
||||
const SearchSectionPolda = () => {
|
||||
const [contentType, setContentType] = useState("all");
|
||||
|
|
@ -35,7 +22,7 @@ const SearchSectionPolda = () => {
|
|||
return (
|
||||
<div className="flex w-full min-h-screen bg-center bg-cover bg-no-repeat" style={{ backgroundImage: "url('/assets/background.png')" }}>
|
||||
<div className="hidden xl:block w-[15%] pr-4 py-5 sticky top-[150px] space-y-4 self-start">
|
||||
<LeftBanner />
|
||||
<AdvertisementPlacements placement="left"/>
|
||||
</div>
|
||||
|
||||
<div className="w-full xl:w-[70%] px-4 py-8 bg-white">
|
||||
|
|
@ -49,7 +36,7 @@ const SearchSectionPolda = () => {
|
|||
</div>
|
||||
|
||||
<div className="hidden xl:block w-[15%] pl-4 py-5 sticky top-[150px] space-y-4 self-start">
|
||||
<RightBanner />
|
||||
<AdvertisementPlacements placement="right"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,20 +13,7 @@ import EventCalender from "./event-calender";
|
|||
import UserSurveyBox from "./survey-box";
|
||||
import ScrollableContentPolda from "./scrollable-content-polda";
|
||||
import ScrollableContentSatker from "./scrollable-content-satker";
|
||||
|
||||
const LeftBanner = () => (
|
||||
<div className="sticky top-0 space-y-4">
|
||||
<img src="/images/all-img/kiri1.png" alt="Banner Kiri 1" />
|
||||
<img src="/images/all-img/kiri2.png" alt="Banner Kiri 2" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const RightBanner = () => (
|
||||
<div className="sticky top-0 space-y-4">
|
||||
<img src="/images/all-img/kanan2.png" alt="Banner Kanan 1" />
|
||||
<img src="/images/all-img/kanan1.png" alt="Banner Kanan 2" />
|
||||
</div>
|
||||
);
|
||||
import AdvertisementPlacements from "./advertisement-placements";
|
||||
|
||||
const SearchSectionSatker = () => {
|
||||
const [contentType, setContentType] = useState("all");
|
||||
|
|
@ -36,7 +23,7 @@ const SearchSectionSatker = () => {
|
|||
return (
|
||||
<div className="flex w-full min-h-screen bg-center bg-cover bg-no-repeat" style={{ backgroundImage: "url('/assets/background.png')" }}>
|
||||
<div className="hidden xl:block w-[15%] pr-4 py-5 sticky top-[150px] space-y-4 self-start">
|
||||
<LeftBanner />
|
||||
<AdvertisementPlacements placement="left"/>
|
||||
</div>
|
||||
|
||||
<div className="w-full xl:w-[70%] px-4 py-8 bg-white">
|
||||
|
|
@ -50,7 +37,7 @@ const SearchSectionSatker = () => {
|
|||
</div>
|
||||
|
||||
<div className="hidden xl:block w-[15%] pl-4 py-5 sticky top-[150px] space-y-4 self-start">
|
||||
<RightBanner />
|
||||
<AdvertisementPlacements placement="right"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,20 +18,7 @@ import ContentCategory from "./content-category";
|
|||
import AreaCoverageWorkUnits from "./area-coverage-and-work-units";
|
||||
import EventCalender from "./event-calender";
|
||||
import UserSurveyBox from "./survey-box";
|
||||
|
||||
const LeftBanner = () => (
|
||||
<div className="sticky top-0 space-y-4 ml-14">
|
||||
<img src="/images/all-img/kiri1.png" alt="Banner Kiri 1" width={180} />
|
||||
<img src="/images/all-img/kiri2.png" alt="Banner Kiri 2" width={180} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const RightBanner = () => (
|
||||
<div className="sticky top-0 space-y-4">
|
||||
<img src="/images/all-img/kanan2.png" alt="Banner Kanan 1" width={180} />
|
||||
<img src="/images/all-img/kanan1.png" alt="Banner Kanan 2" width={180} />
|
||||
</div>
|
||||
);
|
||||
import AdvertisementPlacements from "./advertisement-placements";
|
||||
|
||||
const SearchSection = () => {
|
||||
const [contentType, setContentType] = useState("all");
|
||||
|
|
@ -44,7 +31,7 @@ const SearchSection = () => {
|
|||
style={{ backgroundImage: "url('/assets/background.png')" }}
|
||||
>
|
||||
<div className="hidden xl:block w-[15%] pr-4 py-5 sticky top-[130px] space-y-4 self-start">
|
||||
<LeftBanner />
|
||||
<AdvertisementPlacements placement="left"/>
|
||||
</div>
|
||||
|
||||
<div className="w-full xl:w-[70%] px-4 py-8 bg-white">
|
||||
|
|
@ -58,7 +45,7 @@ const SearchSection = () => {
|
|||
</div>
|
||||
|
||||
<div className="hidden xl:block w-[15%] pl-4 py-5 sticky top-[130px] space-y-4 self-start">
|
||||
<RightBanner />
|
||||
<AdvertisementPlacements placement="right" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -524,18 +524,19 @@ const DetailImage = (data: any) => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="relative self-end ml-auto">
|
||||
<Image
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
||||
width={2560}
|
||||
height={1440}
|
||||
src={detailDataImage?.files[selectedImage]?.url}
|
||||
alt="Main"
|
||||
className="rounded-md w-full h-[670px] object-contain"
|
||||
/>
|
||||
|
||||
<div className="absolute top-4 right-4"></div>
|
||||
</div>
|
||||
<Image
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
)}`}
|
||||
width={2560}
|
||||
height={1440}
|
||||
src={detailDataImage?.files[selectedImage]?.url}
|
||||
alt="Main"
|
||||
className="rounded-md w-full h-[560px] object-contain"
|
||||
/>
|
||||
|
||||
<div className="absolute top-4 right-4"></div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Gambar bawah Kecil */}
|
||||
|
|
@ -1135,7 +1136,7 @@ const DetailImage = (data: any) => {
|
|||
</div>
|
||||
|
||||
{/* Konten Serupa */}
|
||||
<div className="px-4 lg:px-[300px]">
|
||||
<div className="px-4 lg:px-[100px]">
|
||||
<NewContent group="mabes" type={"similar"} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
"use client";
|
||||
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTrigger,
|
||||
} from "../ui/dialog";
|
||||
import { getDataApprovalHistory } from "@/service/curated-content/curated-content";
|
||||
import { ChevronRightIcon } from "lucide-react";
|
||||
|
||||
export default function ApprovalHistoryModal(props: { id: number }) {
|
||||
const { id } = props;
|
||||
const [history, setHistory] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getApprovalHistory();
|
||||
}, []);
|
||||
|
||||
const getApprovalHistory = async () => {
|
||||
const res = await getDataApprovalHistory(id);
|
||||
console.log("res hstro", res?.data?.data);
|
||||
if (res?.data?.data) {
|
||||
setHistory(res?.data?.data);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<a className="mt-2 hover:underline text-primary">
|
||||
Lihat Riwayat Approval
|
||||
</a>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md" className="px-1 lg:px-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-6 font-light text-sm">
|
||||
<div className="col-span-2 flex justify-center">
|
||||
<p className="px-12 py-4 rounded-full bg-cyan-200 ">Upload</p>
|
||||
</div>
|
||||
<div className="col-span-2 col-start-1 flex justify-center">
|
||||
<p className="border-l-2 border-black h-8"></p>
|
||||
</div>
|
||||
{history?.length &&
|
||||
history?.map((list: any, index: number) => (
|
||||
<Fragment key={list.id}>
|
||||
{" "}
|
||||
<div
|
||||
className={`col-span-2 col-start-1 p-4 rounded-lg flex flex-col gap1 ${
|
||||
list.status.id == 2
|
||||
? "bg-cyan-500"
|
||||
: list.status.id == 4
|
||||
? "bg-red-500"
|
||||
: "bg-yellow-500"
|
||||
}`}
|
||||
>
|
||||
<p className="text-white font-semibold mb-2">
|
||||
{list.status.name}
|
||||
</p>
|
||||
<div
|
||||
className={`w-full rounded-lg flex flex-col p-4 ${
|
||||
list.status.id == 2
|
||||
? "bg-cyan-200"
|
||||
: list.status.id == 4
|
||||
? "bg-red-50"
|
||||
: "bg-yellow-100"
|
||||
}`}
|
||||
>
|
||||
<p>Level: {list.levelNumber}</p>
|
||||
<p>Direview oleh: {list.approvalBy.fullname}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-1 hidden md:flex justify-center items-center">
|
||||
<ChevronRightIcon />
|
||||
</div>
|
||||
<div className="col-span-3 flex items-center mt-2 md:mt-0">
|
||||
<div
|
||||
className={`flex flex-col p-4 w-full rounded-lg ${
|
||||
list.status.id == 2
|
||||
? "bg-cyan-200"
|
||||
: list.status.id == 4
|
||||
? "bg-red-50"
|
||||
: "bg-yellow-100"
|
||||
}`}
|
||||
>
|
||||
<p>Catatan :</p>
|
||||
<p>{list.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
{index !== history.length - 1 && (
|
||||
<div className="col-span-2 col-start-1 flex justify-center">
|
||||
<p className="border-l-2 border-black h-8"></p>
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
{history.length > 0 && history[history.length - 1].status.id == 2 && (
|
||||
<>
|
||||
<div className="col-span-2 col-start-1 flex justify-center">
|
||||
<p className="border-l-2 border-black h-8"></p>
|
||||
</div>
|
||||
<div className="col-span-2 col-start-1 flex justify-center">
|
||||
<p className="px-12 py-4 rounded-full bg-green-300 ">Publish</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
"use client";
|
||||
|
||||
import { MailIcon, UserIcon } from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTrigger,
|
||||
} from "../ui/dialog";
|
||||
import { Button } from "../ui/button";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Input } from "../ui/input";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import {
|
||||
createSuggestion,
|
||||
deleteSuggestion,
|
||||
getSuggestionList,
|
||||
} from "@/service/curated-content/curated-content";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
|
||||
export default function SuggestionModal(props: {
|
||||
id: number;
|
||||
numberOfSuggestion: number;
|
||||
}) {
|
||||
const { id, numberOfSuggestion } = props;
|
||||
const t = useTranslations("Form");
|
||||
|
||||
const [suggestionValue, setSuggestionValue] = useState("");
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
|
||||
const [listData, setListData] = useState([]);
|
||||
const [replyId, setReplyId] = useState(0);
|
||||
const [replyValue, setReplyValue] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
initState();
|
||||
}, [id]);
|
||||
|
||||
async function initState() {
|
||||
loading();
|
||||
const response = await getSuggestionList(id);
|
||||
setListData(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
|
||||
const saveSuggestion = async (parentId?: number) => {
|
||||
console.log("suggestion vval", suggestionValue);
|
||||
const data = {
|
||||
mediaUploadId: Number(id),
|
||||
message: parentId ? replyValue : suggestionValue,
|
||||
parentId: parentId ? parentId : null,
|
||||
};
|
||||
loading();
|
||||
const response = await createSuggestion(data);
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
setSuggestionValue("");
|
||||
setReplyValue("");
|
||||
initState();
|
||||
};
|
||||
|
||||
const handleDeleteSuggestion = async (id: number) => {
|
||||
loading();
|
||||
const response = await deleteSuggestion(id);
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
initState();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<a className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm cursor-pointer">
|
||||
<MailIcon />
|
||||
<p className="">
|
||||
{t("suggestion-box")} ({numberOfSuggestion || 0})
|
||||
</p>
|
||||
</a>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md" className="px-1 lg:px-4">
|
||||
<DialogHeader>Suggestions</DialogHeader>
|
||||
|
||||
<div className="flex flex-row items-center justify-center">
|
||||
<Input
|
||||
value={suggestionValue}
|
||||
onChange={(e) => setSuggestionValue(e.target.value)}
|
||||
/>
|
||||
<Button size="sm" onClick={() => saveSuggestion()}>
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{listData?.length > 0 &&
|
||||
listData?.map((data: any) => (
|
||||
<div key={data?.id} className="flex flex-col gap-1 w-full">
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="mt-2 w-[24px] h-[24px] md:w-[50px] md:h-[50px] border-2 flex items-center justify-center rounded-full bg-stone-200 border-stone-300">
|
||||
<UserIcon size={36} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between bg-stone-200 px-2 lg:px-4 py-2 w-full rounded-lg">
|
||||
<div className="flex flex-col gap-1">
|
||||
<b className="text-sm md:text-base">
|
||||
{data.suggestionFrom?.fullname}
|
||||
</b>
|
||||
<p className="text-xs md:text-sm">{data.message}</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-xs md:text-sm text-right">
|
||||
{formatDateToIndonesian(data.createdAt)}
|
||||
</p>
|
||||
<div className="flex flex-col items-end md:flex-row gap-2 text-xs md:text-sm justify-end">
|
||||
<a
|
||||
className="text-primary cursor-pointer"
|
||||
onClick={() => {
|
||||
setReplyId(replyId === data?.id ? 0 : data?.id);
|
||||
setReplyValue("");
|
||||
}}
|
||||
>
|
||||
Balas
|
||||
</a>
|
||||
<a
|
||||
onClick={() => handleDeleteSuggestion(data?.id)}
|
||||
className="text-destructive cursor-pointer"
|
||||
>
|
||||
Hapus
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{replyId === data?.id && (
|
||||
<div className="flex flex-row items-center justify-center">
|
||||
<Input
|
||||
value={replyValue}
|
||||
onChange={(e) => setReplyValue(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => saveSuggestion(data?.id)}
|
||||
>
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{data?.children.length > 0 &&
|
||||
data?.children.map((child: any) => (
|
||||
<div key={child.id} className="flex flex-row gap-2">
|
||||
<div className="w-[50px]"></div>
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<div className="flex flex-row gap-2 w-full">
|
||||
<div className="mt-2 w-[24px] h-[24px] md:w-[50px] md:h-[50px] border-2 flex items-center justify-center rounded-full bg-stone-200 border-stone-300">
|
||||
<UserIcon size={36} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between bg-stone-100 px-2 lg:px-4 py-2 w-full rounded-lg">
|
||||
<div className="flex flex-col gap-1">
|
||||
<b className="text-xs md:text-base">
|
||||
{child.suggestionFrom?.fullname}
|
||||
</b>
|
||||
<p className="text-xs md:text-sm">
|
||||
{child.message}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-xs md:text-sm text-right">
|
||||
{formatDateToIndonesian(child.createdAt)}
|
||||
</p>
|
||||
<div className="flex flex-col items-end md:flex-row gap-2 text-xs md:text-sm justify-end">
|
||||
<a
|
||||
className="text-primary cursor-pointer"
|
||||
onClick={() => {
|
||||
setReplyId(
|
||||
replyId === child?.id ? 0 : child?.id
|
||||
);
|
||||
setReplyValue("");
|
||||
}}
|
||||
>
|
||||
Balas
|
||||
</a>
|
||||
<a
|
||||
onClick={() =>
|
||||
handleDeleteSuggestion(child?.id)
|
||||
}
|
||||
className="text-destructive cursor-pointer"
|
||||
>
|
||||
Hapus
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{replyId === child?.id && (
|
||||
<div className="flex flex-row items-center justify-center">
|
||||
<Input
|
||||
value={replyValue}
|
||||
onChange={(e) =>
|
||||
setReplyValue(e.target.value)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => saveSuggestion(child?.id)}
|
||||
>
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{child?.children.length > 0 &&
|
||||
child?.children.map((items: any) => (
|
||||
<div key={items.id} className="flex flex-row gap-2">
|
||||
<div className="w-[50px]"></div>
|
||||
<div className="flex flex-row gap-2 w-full">
|
||||
<div className="mt-2 w-[24px] h-[24px] md:w-[50px] md:h-[50px] border-2 flex items-center justify-center rounded-full bg-stone-200 border-stone-300">
|
||||
<UserIcon size={36} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between bg-stone-50 px-2 lg:px-4 py-2 w-full rounded-lg">
|
||||
<div className="flex flex-col gap-1">
|
||||
<b className="text-xs md:text-base">
|
||||
{items.suggestionFrom?.fullname}
|
||||
</b>
|
||||
<p className="text-xs md:text-sm">
|
||||
{items.message}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-xs md:text-sm text-right">
|
||||
{formatDateToIndonesian(
|
||||
items.createdAt
|
||||
)}
|
||||
</p>
|
||||
<div className="flex flex-col items-end md:flex-row gap-2 text-xs md:text-sm justify-end">
|
||||
<a
|
||||
onClick={() =>
|
||||
handleDeleteSuggestion(items?.id)
|
||||
}
|
||||
className="text-destructive cursor-pointer"
|
||||
>
|
||||
Hapus
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -279,6 +279,7 @@ const LoginForm = () => {
|
|||
return false;
|
||||
}
|
||||
const msg = response?.data?.message;
|
||||
|
||||
if (msg == "Continue to setup email") {
|
||||
setStep(2);
|
||||
} else if (msg == "Email is valid and OTP has been sent") {
|
||||
|
|
|
|||
36
lib/menus.ts
36
lib/menus.ts
|
|
@ -1804,6 +1804,42 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// groupLabel: "",
|
||||
// id: "schedule",
|
||||
// menus: [
|
||||
// {
|
||||
// id: "schedule",
|
||||
// href: "/contributor/schedule",
|
||||
// label: t("schedule"),
|
||||
// active: pathname.includes("/schedule"),
|
||||
// icon: "uil:schedule",
|
||||
// submenus: [
|
||||
// {
|
||||
// href: "/contributor/schedule/press-conference",
|
||||
// label: t("press-conference"),
|
||||
// active: pathname.includes("/schedule/press-conference"),
|
||||
// icon: "heroicons:arrow-trending-up",
|
||||
// children: [],
|
||||
// },
|
||||
// {
|
||||
// href: "/contributor/schedule/event",
|
||||
// label: t("event"),
|
||||
// active: pathname.includes("/schedule/event"),
|
||||
// icon: "heroicons:shopping-cart",
|
||||
// children: [],
|
||||
// },
|
||||
// {
|
||||
// href: "/contributor/schedule/press-release",
|
||||
// label: t("press-release"),
|
||||
// active: pathname.includes("/schedule/press-release"),
|
||||
// icon: "heroicons:shopping-cart",
|
||||
// children: [],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "schedule",
|
||||
|
|
|
|||
|
|
@ -690,7 +690,7 @@
|
|||
"type-task": "Tipe Penugasan",
|
||||
"category-task": "Kategori Penugasan",
|
||||
"areas-expertise": "Bidang Keahlian",
|
||||
"choose-expert": "Pilih Tenaga Ahli",
|
||||
"choose-expert": "Tenaga Ahli",
|
||||
"code": "Kode",
|
||||
"start-date": "Tanggal Mulai",
|
||||
"end-date": "Tanggal Selesai",
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@ export async function listDataAdvertisements(
|
|||
page: number,
|
||||
limit: string,
|
||||
search: string,
|
||||
categoryFilter: string,
|
||||
statusFilter: string
|
||||
) {
|
||||
const name = search || "";
|
||||
const url = `advertisements/pagination?title=${name}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&categoryId=${categoryFilter}&statusId=${statusFilter}`;
|
||||
const url = `advertisements/pagination?title=${search}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,3 +35,27 @@ export async function deleteMediaCurationMessage(id: any) {
|
|||
const url = `media/curation/message?id=${id}`;
|
||||
return httpDeleteInterceptor(url);
|
||||
}
|
||||
|
||||
export async function getSuggestionList(id: number | string) {
|
||||
const url = `media/suggestion?mediaId=${id}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
||||
export async function createSuggestion(data: any) {
|
||||
const url = "media/suggestion";
|
||||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function deleteSuggestion(id: number | string) {
|
||||
const url = `media/suggestion?id=${id}`;
|
||||
return httpDeleteInterceptor(url);
|
||||
}
|
||||
|
||||
export async function getDataApprovalByMediaUpload(id: number | string) {
|
||||
const url = `media/approval?mediaUploadId=${id}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
export async function getDataApprovalHistory(id: number | string) {
|
||||
const url = `media/approval/history?id=${id}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,16 @@ export async function paginationSchedule(
|
|||
);
|
||||
}
|
||||
|
||||
export async function paginationCalendar(
|
||||
export async function getCalendarPagination(
|
||||
size: any,
|
||||
page: number,
|
||||
type: any,
|
||||
title: string = "",
|
||||
statusFilter: number[] = []
|
||||
) {
|
||||
const statusQuery =
|
||||
statusFilter.length > 0 ? `&statusId=${statusFilter.join(",")}` : "";
|
||||
return await httpGetInterceptor(
|
||||
`calendars/pagination?enablePage=1&scheduleTypeId=${type}&page=${page}&size=${size}&title=${title}${statusQuery}`
|
||||
`calendars/pagination?enablePage=1&page=${page}&size=${size}&title=${title}${statusQuery}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +38,11 @@ export async function postSchedule(data: any) {
|
|||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function deleteSchedule(id: string | number) {
|
||||
const url = `schedule?id=${id}`;
|
||||
return httpDeleteInterceptor(url);
|
||||
}
|
||||
|
||||
export async function detailSchedule(id: any) {
|
||||
const url = `public/schedule?id=${id}`;
|
||||
return httpGetInterceptor(url);
|
||||
|
|
@ -98,3 +102,8 @@ export async function deleteCalendar(id: any) {
|
|||
const url = `calendars?id=${id}`;
|
||||
return httpDeleteInterceptor(url);
|
||||
}
|
||||
|
||||
export async function postApprovalSchedule(data: any) {
|
||||
const url = "schedule/approval";
|
||||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,5 +132,8 @@ export async function getTagsByParentId(parentId: string | number) {
|
|||
|
||||
export async function postAdvertisements(data: any) {
|
||||
const url = "advertisements";
|
||||
return httpPostInterceptor(url, data);
|
||||
const headers = {
|
||||
"Content-Type": "multipart/form-data",
|
||||
};
|
||||
return httpPostInterceptor(url, data, headers);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ export async function listTaskTa(
|
|||
}
|
||||
|
||||
return httpGetInterceptor(
|
||||
`assignment-expert/pagination?enablePage=1&size=${size}&page=${page}&title=${title}&taskType=${taskType}&uniqueCode=${code}&createdAt=${createdAt}${statusQuery}`
|
||||
`assignment-expert/pagination?enablePage=1&size=${size}&page=${page}&title=${title}&assignmentType=${taskType}&uniqueCode=${code}&createdAt=${createdAt}${statusQuery}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@
|
|||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@
|
|||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
|
|
|
|||
Loading…
Reference in New Issue