Merge branch 'main' of https://gitlab.com/hanifsalafi/mediahub_redesign into dev-rama
This commit is contained in:
commit
2fb9bf047b
|
|
@ -1,10 +1,5 @@
|
||||||
// "use client";
|
// "use client";
|
||||||
import React, {
|
import React, { useState, useEffect, useRef, Fragment } from "react";
|
||||||
useState,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
Fragment,
|
|
||||||
} from "react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
@ -49,6 +44,7 @@ import { getUserLevelForAssignments } from "@/service/task";
|
||||||
import { AudioRecorder } from "react-audio-voice-recorder";
|
import { AudioRecorder } from "react-audio-voice-recorder";
|
||||||
import FileUploader from "@/components/form/shared/file-uploader";
|
import FileUploader from "@/components/form/shared/file-uploader";
|
||||||
import { Upload } from "tus-js-client";
|
import { Upload } from "tus-js-client";
|
||||||
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
title: z.string().min(3, { message: "Required" }),
|
title: z.string().min(3, { message: "Required" }),
|
||||||
|
|
@ -75,9 +71,7 @@ const EventModal = ({
|
||||||
const [startDate, setStartDate] = useState<Date>(new Date());
|
const [startDate, setStartDate] = useState<Date>(new Date());
|
||||||
const [endDate, setEndDate] = useState<Date>(new Date());
|
const [endDate, setEndDate] = useState<Date>(new Date());
|
||||||
const [isPending, startTransition] = React.useTransition();
|
const [isPending, startTransition] = React.useTransition();
|
||||||
const [agendaType, setAgendaType] = React.useState<any>(
|
const [agendaType, setAgendaType] = React.useState<any>(categories[0].value);
|
||||||
categories[0].value
|
|
||||||
);
|
|
||||||
const [listDest, setListDest] = useState([]);
|
const [listDest, setListDest] = useState([]);
|
||||||
const [deleteModalOpen, setDeleteModalOpen] = useState<boolean>(false);
|
const [deleteModalOpen, setDeleteModalOpen] = useState<boolean>(false);
|
||||||
const [eventIdToDelete, setEventIdToDelete] = useState<string | null>(null);
|
const [eventIdToDelete, setEventIdToDelete] = useState<string | null>(null);
|
||||||
|
|
@ -97,6 +91,12 @@ const EventModal = ({
|
||||||
const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false);
|
const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false);
|
||||||
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
|
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
|
||||||
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
|
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
|
||||||
|
let progressInfo: any = [];
|
||||||
|
let counterUpdateProgress = 0;
|
||||||
|
const [progressList, setProgressList] = useState<any>([]);
|
||||||
|
let uploadPersen = 0;
|
||||||
|
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||||
|
const [counterProgress, setCounterProgress] = useState(0);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
|
@ -118,11 +118,17 @@ const EventModal = ({
|
||||||
const levelList = response?.data?.data.list;
|
const levelList = response?.data?.data.list;
|
||||||
let listFiltered = [];
|
let listFiltered = [];
|
||||||
if (agendaType == "polda") {
|
if (agendaType == "polda") {
|
||||||
listFiltered = levelList.filter((level: any) => level.name != 'SATKER POLRI');
|
listFiltered = levelList.filter(
|
||||||
|
(level: any) => level.name != "SATKER POLRI"
|
||||||
|
);
|
||||||
} else if (agendaType == "polres") {
|
} else if (agendaType == "polres") {
|
||||||
listFiltered = levelList.filter((level: any) => level.name != 'SATKER POLRI');
|
listFiltered = levelList.filter(
|
||||||
|
(level: any) => level.name != "SATKER POLRI"
|
||||||
|
);
|
||||||
} else if (agendaType == "satker") {
|
} else if (agendaType == "satker") {
|
||||||
listFiltered = levelList.filter((level: any) => level.name == 'SATKER POLRI');
|
listFiltered = levelList.filter(
|
||||||
|
(level: any) => level.name == "SATKER POLRI"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
setListDest(listFiltered);
|
setListDest(listFiltered);
|
||||||
const initialExpandedState = listFiltered.reduce(
|
const initialExpandedState = listFiltered.reduce(
|
||||||
|
|
@ -181,52 +187,28 @@ const EventModal = ({
|
||||||
setIsImageUploadFinish(true);
|
setIsImageUploadFinish(true);
|
||||||
}
|
}
|
||||||
imageFiles?.map(async (item: any, index: number) => {
|
imageFiles?.map(async (item: any, index: number) => {
|
||||||
await uploadResumableFile(
|
await uploadResumableFile(index, String(id), item, "1", "0");
|
||||||
index,
|
|
||||||
String(id),
|
|
||||||
item,
|
|
||||||
"1",
|
|
||||||
"0"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (videoFiles?.length == 0) {
|
if (videoFiles?.length == 0) {
|
||||||
setIsVideoUploadFinish(true);
|
setIsVideoUploadFinish(true);
|
||||||
}
|
}
|
||||||
videoFiles?.map(async (item: any, index: number) => {
|
videoFiles?.map(async (item: any, index: number) => {
|
||||||
await uploadResumableFile(
|
await uploadResumableFile(index, String(id), item, "2", "0");
|
||||||
index,
|
|
||||||
String(id),
|
|
||||||
item,
|
|
||||||
"2",
|
|
||||||
"0"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (textFiles?.length == 0) {
|
if (textFiles?.length == 0) {
|
||||||
setIsTextUploadFinish(true);
|
setIsTextUploadFinish(true);
|
||||||
}
|
}
|
||||||
textFiles?.map(async (item: any, index: number) => {
|
textFiles?.map(async (item: any, index: number) => {
|
||||||
await uploadResumableFile(
|
await uploadResumableFile(index, String(id), item, "3", "0");
|
||||||
index,
|
|
||||||
String(id),
|
|
||||||
item,
|
|
||||||
"3",
|
|
||||||
"0"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (audioFiles?.length == 0) {
|
if (audioFiles?.length == 0) {
|
||||||
setIsAudioUploadFinish(true);
|
setIsAudioUploadFinish(true);
|
||||||
}
|
}
|
||||||
audioFiles?.map(async (item: any, index: number) => {
|
audioFiles?.map(async (item: any, index: number) => {
|
||||||
await uploadResumableFile(
|
await uploadResumableFile(index, String(id), item, "4", "0");
|
||||||
index,
|
|
||||||
String(id),
|
|
||||||
item,
|
|
||||||
"4",
|
|
||||||
"0"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Optional: Use Swal for success feedback
|
// Optional: Use Swal for success feedback
|
||||||
|
|
@ -325,19 +307,24 @@ const EventModal = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
async function uploadResumableFile(
|
async function uploadResumableFile(
|
||||||
idx: number,
|
idx: number,
|
||||||
id: string,
|
id: string,
|
||||||
file: any,
|
file: any,
|
||||||
fileTypeId: string,
|
fileTypeId: string,
|
||||||
duration: string
|
duration: string
|
||||||
) {
|
) {
|
||||||
console.log(idx, id, file, fileTypeId, duration);
|
console.log(idx, id, file, fileTypeId, duration);
|
||||||
|
|
||||||
// const placements = getPlacement(file.placements);
|
const resCsrf = await getCsrfToken();
|
||||||
// console.log("Placementttt: : ", placements);
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
console.log("CSRF TOKEN : ", csrfToken);
|
||||||
|
const headers = {
|
||||||
|
"X-XSRF-TOKEN": csrfToken,
|
||||||
|
};
|
||||||
|
|
||||||
const upload = new Upload(file, {
|
const upload = new Upload(file, {
|
||||||
endpoint: `${process.env.NEXT_PUBLIC_API}/agenda-settings/file/upload`,
|
endpoint: `${process.env.NEXT_PUBLIC_API}/agenda-settings/file/upload`,
|
||||||
|
headers: headers,
|
||||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||||
chunkSize: 20_000,
|
chunkSize: 20_000,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -345,8 +332,12 @@ const EventModal = ({
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
filetype: file.type,
|
filetype: file.type,
|
||||||
fileTypeId: fileTypeId,
|
fileTypeId: fileTypeId,
|
||||||
duration: "",
|
duration,
|
||||||
isWatermark: "true", // hardcode
|
isWatermark: "false", // hardcode
|
||||||
|
},
|
||||||
|
onBeforeRequest: function (req) {
|
||||||
|
var xhr = req.getUnderlyingObject();
|
||||||
|
xhr.withCredentials = true;
|
||||||
},
|
},
|
||||||
onError: async (e: any) => {
|
onError: async (e: any) => {
|
||||||
console.log("Error upload :", e);
|
console.log("Error upload :", e);
|
||||||
|
|
@ -358,27 +349,18 @@ const EventModal = ({
|
||||||
bytesTotal: any
|
bytesTotal: any
|
||||||
) => {
|
) => {
|
||||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||||
// progressInfo[idx].percentage = uploadPersen;
|
progressInfo[idx].percentage = uploadPersen;
|
||||||
// counterUpdateProgress++;
|
counterUpdateProgress++;
|
||||||
// console.log(counterUpdateProgress);
|
console.log(counterUpdateProgress);
|
||||||
// setProgressList(progressInfo);
|
setProgressList(progressInfo);
|
||||||
// setCounterProgress(counterUpdateProgress);
|
setCounterProgress(counterUpdateProgress);
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
// uploadPersen = 100;
|
uploadPersen = 100;
|
||||||
// progressInfo[idx].percentage = 100;
|
progressInfo[idx].percentage = 100;
|
||||||
// counterUpdateProgress++;
|
counterUpdateProgress++;
|
||||||
// setCounterProgress(counterUpdateProgress);
|
setCounterProgress(counterUpdateProgress);
|
||||||
successTodo();
|
successTodo();
|
||||||
if (fileTypeId == '1'){
|
|
||||||
setIsImageUploadFinish(true);
|
|
||||||
} else if (fileTypeId == '2'){
|
|
||||||
setIsVideoUploadFinish(true);
|
|
||||||
} if (fileTypeId == '3'){
|
|
||||||
setIsTextUploadFinish(true);
|
|
||||||
} if (fileTypeId == '4'){
|
|
||||||
setIsAudioUploadFinish(true);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -387,14 +369,24 @@ const EventModal = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
successTodo();
|
successTodo();
|
||||||
}, [isImageUploadFinish, isVideoUploadFinish, isAudioUploadFinish, isTextUploadFinish])
|
}, [
|
||||||
|
isImageUploadFinish,
|
||||||
|
isVideoUploadFinish,
|
||||||
|
isAudioUploadFinish,
|
||||||
|
isTextUploadFinish,
|
||||||
|
]);
|
||||||
|
|
||||||
function successTodo() {
|
function successTodo() {
|
||||||
if (isImageUploadFinish && isVideoUploadFinish && isAudioUploadFinish && isTextUploadFinish) {
|
if (
|
||||||
|
isImageUploadFinish &&
|
||||||
|
isVideoUploadFinish &&
|
||||||
|
isAudioUploadFinish &&
|
||||||
|
isTextUploadFinish
|
||||||
|
) {
|
||||||
successSubmit("/in/contributor/agenda-setting");
|
successSubmit("/in/contributor/agenda-setting");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const successSubmit = (redirect: string) => {
|
const successSubmit = (redirect: string) => {
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Sukses",
|
title: "Sukses",
|
||||||
|
|
@ -422,7 +414,9 @@ const EventModal = ({
|
||||||
>
|
>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{event?.length > 1 ? "Edit Agenda Setting" : "Create Agenda Setting"}{" "}
|
{event?.length > 1
|
||||||
|
? "Edit Agenda Setting"
|
||||||
|
: "Create Agenda Setting"}{" "}
|
||||||
{event?.title}
|
{event?.title}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
@ -551,7 +545,9 @@ const EventModal = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{(agendaType === "polda" || agendaType === "polres" || agendaType === "satker" )&& (
|
{(agendaType === "polda" ||
|
||||||
|
agendaType === "polres" ||
|
||||||
|
agendaType === "satker") && (
|
||||||
<div>
|
<div>
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
|
|
@ -588,50 +584,56 @@ const EventModal = ({
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</Label>
|
</Label>
|
||||||
{(agendaType == "polres" || agendaType == "satker") && expandedPolda[polda.id] && (
|
{(agendaType == "polres" ||
|
||||||
<div className="ml-6 mt-2">
|
agendaType == "satker") &&
|
||||||
<Label className="block">
|
expandedPolda[polda.id] && (
|
||||||
<Checkbox
|
<div className="ml-6 mt-2">
|
||||||
checked={polda?.subDestination?.every(
|
<Label className="block">
|
||||||
(polres: any) =>
|
|
||||||
checkedLevels.has(polres.id)
|
|
||||||
)}
|
|
||||||
onCheckedChange={(isChecked) => {
|
|
||||||
const updatedLevels = new Set(
|
|
||||||
checkedLevels
|
|
||||||
);
|
|
||||||
polda?.subDestination?.forEach(
|
|
||||||
(polres: any) => {
|
|
||||||
if (isChecked) {
|
|
||||||
updatedLevels.add(polres.id);
|
|
||||||
} else {
|
|
||||||
updatedLevels.delete(polres.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
setCheckedLevels(updatedLevels);
|
|
||||||
}}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
Pilih Semua Polres
|
|
||||||
</Label>
|
|
||||||
{polda?.subDestination?.map((polres: any) => (
|
|
||||||
<Label
|
|
||||||
key={polres.id}
|
|
||||||
className="block mt-1"
|
|
||||||
>
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checkedLevels.has(polres.id)}
|
checked={polda?.subDestination?.every(
|
||||||
onCheckedChange={() =>
|
(polres: any) =>
|
||||||
handleCheckboxChange(polres.id)
|
checkedLevels.has(polres.id)
|
||||||
}
|
)}
|
||||||
|
onCheckedChange={(isChecked) => {
|
||||||
|
const updatedLevels = new Set(
|
||||||
|
checkedLevels
|
||||||
|
);
|
||||||
|
polda?.subDestination?.forEach(
|
||||||
|
(polres: any) => {
|
||||||
|
if (isChecked) {
|
||||||
|
updatedLevels.add(polres.id);
|
||||||
|
} else {
|
||||||
|
updatedLevels.delete(polres.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setCheckedLevels(updatedLevels);
|
||||||
|
}}
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
{polres.name}
|
Pilih Semua Polres
|
||||||
</Label>
|
</Label>
|
||||||
))}
|
{polda?.subDestination?.map(
|
||||||
</div>
|
(polres: any) => (
|
||||||
)}
|
<Label
|
||||||
|
key={polres.id}
|
||||||
|
className="block mt-1"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={checkedLevels.has(
|
||||||
|
polres.id
|
||||||
|
)}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleCheckboxChange(polres.id)
|
||||||
|
}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
{polres.name}
|
||||||
|
</Label>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -665,7 +667,7 @@ const EventModal = ({
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<Label>Video</Label>
|
<Label>Video</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"mp4/*": [],
|
"mp4/*": [],
|
||||||
"mov/*": [],
|
"mov/*": [],
|
||||||
|
|
@ -677,7 +679,7 @@ const EventModal = ({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>Foto</Label>
|
<Label>Foto</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"image/*": [],
|
"image/*": [],
|
||||||
}}
|
}}
|
||||||
|
|
@ -688,7 +690,7 @@ const EventModal = ({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>Teks</Label>
|
<Label>Teks</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"pdf/*": [],
|
"pdf/*": [],
|
||||||
}}
|
}}
|
||||||
|
|
@ -708,7 +710,7 @@ const EventModal = ({
|
||||||
downloadOnSavePress={true}
|
downloadOnSavePress={true}
|
||||||
downloadFileExtension="webm"
|
downloadFileExtension="webm"
|
||||||
/>
|
/>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"mp3/*": [],
|
"mp3/*": [],
|
||||||
"wav/*": [],
|
"wav/*": [],
|
||||||
|
|
@ -744,9 +746,9 @@ const EventModal = ({
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="me-2 h-4 w-4 animate-spin" />
|
<Loader2 className="me-2 h-4 w-4 animate-spin" />
|
||||||
{event?.length > 1 ? "Updating..." : "Adding..."}
|
{event?.length > 1 ? "Updating..." : "Adding..."}
|
||||||
</>
|
</>
|
||||||
) : event?.length > 1 ? (
|
) : event?.length > 1 ? (
|
||||||
"Update Agenda Setting"
|
"Update Agenda Setting"
|
||||||
) : (
|
) : (
|
||||||
"Simpan Agenda Setting"
|
"Simpan Agenda Setting"
|
||||||
|
|
|
||||||
|
|
@ -112,12 +112,17 @@ const columns: ColumnDef<any>[] = [
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
|
const isDisabled = row.original.isPublish; // Check the isPublish value
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild disabled={isDisabled}>
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
className={`bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent ${
|
||||||
|
isDisabled ? "cursor-not-allowed opacity-50" : ""
|
||||||
|
}`}
|
||||||
|
disabled={isDisabled} // Disable button if isPublish is true
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open menu</span>
|
<span className="sr-only">Open menu</span>
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
|
@ -127,7 +132,12 @@ const columns: ColumnDef<any>[] = [
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/content/spit/convert/${row.original.contentId}`}
|
href={`/contributor/content/spit/convert/${row.original.contentId}`}
|
||||||
>
|
>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<DropdownMenuItem
|
||||||
|
className={`p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none ${
|
||||||
|
isDisabled ? "cursor-not-allowed opacity-50" : ""
|
||||||
|
}`}
|
||||||
|
disabled={isDisabled} // Disable dropdown item if isPublish is true
|
||||||
|
>
|
||||||
<MoveDownRight className="w-4 h-4 me-1.5" />
|
<MoveDownRight className="w-4 h-4 me-1.5" />
|
||||||
Pindah Ke Mediahub
|
Pindah Ke Mediahub
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,23 @@ const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "title",
|
accessorKey: "title",
|
||||||
header: "Title",
|
header: "Title",
|
||||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
cell: ({ row }) => (
|
||||||
|
<div>
|
||||||
|
<span>{row.getValue("title")}</span>
|
||||||
|
{row.original.isForward && (
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
className="ml-3 rounded-xl"
|
||||||
|
>
|
||||||
|
Forward
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "uniqueCode",
|
accessorKey: "uniqueCode",
|
||||||
header: "Code",
|
header: "Code",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTask from "@/components/form/task/task-form";
|
||||||
|
import FormTaskDetail from "@/components/form/task/task-detail-form";
|
||||||
|
import FormTaskForward from "@/components/form/task/task-forward-form";
|
||||||
|
|
||||||
|
const TaskForwardPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormTaskForward />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskForwardPage;
|
||||||
|
|
@ -21,8 +21,19 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { postBlog } from "@/service/blog/blog";
|
import { postBlog } from "@/service/blog/blog";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { DotSquare, InboxIcon, PaperclipIcon, SmileIcon } from "lucide-react";
|
import {
|
||||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
DotSquare,
|
||||||
|
InboxIcon,
|
||||||
|
PaperclipIcon,
|
||||||
|
SmileIcon,
|
||||||
|
TrashIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import {
|
||||||
|
deleteMediaCurationMessage,
|
||||||
|
detailMedia,
|
||||||
|
getMediaCurationMessage,
|
||||||
|
saveMediaCurationMessage,
|
||||||
|
} from "@/service/curated-content/curated-content";
|
||||||
import { Swiper, SwiperSlide } from "swiper/react";
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "swiper/css/free-mode";
|
import "swiper/css/free-mode";
|
||||||
|
|
@ -34,6 +45,14 @@ import "swiper/css/navigation";
|
||||||
import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
|
import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
|
||||||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { listData } from "@/service/landing/landing";
|
||||||
|
import {
|
||||||
|
createAssignmentResponse,
|
||||||
|
deleteAssignmentResponse,
|
||||||
|
getAssignmentResponseList,
|
||||||
|
} from "@/service/task";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { close, loading } from "@/lib/swal";
|
||||||
|
|
||||||
const detailSchema = z.object({
|
const detailSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -50,6 +69,24 @@ type Category = {
|
||||||
categoryName: string;
|
categoryName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatDate = (dateString: string): string => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
|
||||||
|
// Pastikan validitas tanggal
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
throw new Error("Invalid date format");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format tanggal
|
||||||
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
|
const year = date.getFullYear();
|
||||||
|
// const hours = date.getHours().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
// Gabungkan hasil format
|
||||||
|
return `${day}-${month}-${year} `;
|
||||||
|
};
|
||||||
|
|
||||||
export type curationDetail = {
|
export type curationDetail = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -108,22 +145,21 @@ export default function DetailImage() {
|
||||||
console.log(id);
|
console.log(id);
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
type DetailSchema = z.infer<typeof detailSchema>;
|
type DetailSchema = z.infer<typeof detailSchema>;
|
||||||
|
const userLevelNumber = getCookiesDecrypt("ulne");
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
const scheduleType = Cookies.get("scheduleType");
|
const scheduleType = Cookies.get("scheduleType");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
// const [detail, setDetail] = useState({
|
|
||||||
// title: null,
|
|
||||||
// tags: null,
|
|
||||||
// files: [],
|
|
||||||
// fileType: null,
|
|
||||||
// });
|
|
||||||
const [detail, setDetail] = useState<curationDetail>();
|
const [detail, setDetail] = useState<curationDetail>();
|
||||||
const [refresh] = useState(false);
|
const [refresh] = useState(false);
|
||||||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
const [showInput, setShowInput] = useState<boolean>(false);
|
||||||
|
const [selectedFileId, setSelectedFileId] = useState(null);
|
||||||
|
const [listData, setListData] = useState([]);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|
@ -142,31 +178,93 @@ export default function DetailImage() {
|
||||||
setReplyingTo(commentId);
|
setReplyingTo(commentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReply = (commentId: number) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
if (replyText.trim()) {
|
setMessage(e.target.value);
|
||||||
const newCommentData = commentsData.map((comment: any) => {
|
};
|
||||||
if (comment.id === commentId) {
|
|
||||||
return {
|
|
||||||
...comment,
|
|
||||||
replies: [
|
|
||||||
...comment.replies,
|
|
||||||
{
|
|
||||||
text: replyText,
|
|
||||||
username: "You",
|
|
||||||
date: new Date().toLocaleString(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return comment;
|
|
||||||
});
|
|
||||||
|
|
||||||
setCommentsData(newCommentData);
|
useEffect(() => {
|
||||||
setReplyText("");
|
async function initState() {
|
||||||
|
// loading();
|
||||||
|
const response = await getMediaCurationMessage(selectedFileId);
|
||||||
|
console.log("data", response?.data?.data);
|
||||||
|
console.log("userLvl", userLevelNumber);
|
||||||
|
setListData(response?.data?.data);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}, [selectedFileId]);
|
||||||
|
|
||||||
|
// const postData = () => {
|
||||||
|
// sendSuggestionParent();
|
||||||
|
// };
|
||||||
|
|
||||||
|
const postData = async () => {
|
||||||
|
if (message?.length > 1 && selectedFileId) {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
mediaUploadFileId: selectedFileId,
|
||||||
|
message,
|
||||||
|
parentId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await saveMediaCurationMessage(data);
|
||||||
|
console.log("Komentar terkirim:", response);
|
||||||
|
|
||||||
|
const responseGet = await getMediaCurationMessage(selectedFileId);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
|
||||||
|
setMessage("");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error posting comment:", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("Pesan atau file ID tidak valid.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendReplyData = async (parentId: number) => {
|
||||||
|
const inputElement = document.querySelector(
|
||||||
|
`#input-comment-${parentId}`
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
|
||||||
|
if (inputElement?.value?.length > 1 && selectedFileId) {
|
||||||
|
loading();
|
||||||
|
const data = {
|
||||||
|
mediaUploadFileId: selectedFileId,
|
||||||
|
message: inputElement.value,
|
||||||
|
parentId,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Sending reply:", data);
|
||||||
|
const response = await saveMediaCurationMessage(data);
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
const responseGet = await getMediaCurationMessage(selectedFileId);
|
||||||
|
console.log("Updated comments:", responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
|
||||||
|
inputElement.value = "";
|
||||||
|
close();
|
||||||
setReplyingTo(null);
|
setReplyingTo(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function deleteDataSuggestion(dataId: any) {
|
||||||
|
loading();
|
||||||
|
const response = await deleteMediaCurationMessage(dataId);
|
||||||
|
console.log(response);
|
||||||
|
const responseGet = await getMediaCurationMessage(selectedFileId);
|
||||||
|
console.log(responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteData = (dataId: any) => {
|
||||||
|
deleteDataSuggestion(dataId);
|
||||||
|
console.log(dataId);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|
@ -174,16 +272,29 @@ export default function DetailImage() {
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
|
setSelectedFileId(details?.files[0]?.id);
|
||||||
const filesData = details.files || [];
|
const filesData = details.files || [];
|
||||||
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) =>
|
const fileUrls = filesData.map((file: any) => ({
|
||||||
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
|
id: file.id,
|
||||||
);
|
thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg",
|
||||||
|
}));
|
||||||
setDetailThumb(fileUrls);
|
setDetailThumb(fileUrls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initState();
|
initState();
|
||||||
}, [id, refresh]);
|
}, [id, refresh]);
|
||||||
|
|
||||||
|
const handleFileClick = async (fileId: any) => {
|
||||||
|
setSelectedFileId(fileId);
|
||||||
|
try {
|
||||||
|
const response = await getMediaCurationMessage(fileId);
|
||||||
|
console.log("Data komentar:", response?.data?.data);
|
||||||
|
setListData(response?.data?.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching comments:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-10">
|
<div className="flex gap-10">
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -336,11 +447,14 @@ export default function DetailImage() {
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{detailThumb?.map((data: any) => (
|
{detailThumb?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide
|
||||||
|
key={data.id}
|
||||||
|
onClick={() => handleFileClick(data.id)}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
className="object-fill h-full w-full"
|
className="object-fill h-full w-full"
|
||||||
src={data}
|
src={data.thumbnailFileUrl}
|
||||||
alt={` ${data.id}`}
|
alt={`File ID: ${data.id}`}
|
||||||
/>
|
/>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
|
|
@ -357,11 +471,14 @@ export default function DetailImage() {
|
||||||
// className="mySwiper2"
|
// className="mySwiper2"
|
||||||
>
|
>
|
||||||
{detailThumb?.map((data: any) => (
|
{detailThumb?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide
|
||||||
|
key={data.id}
|
||||||
|
onClick={() => handleFileClick(data.id)}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
className="object-cover h-[60px] w-[80px]"
|
className="object-fill h-full w-full"
|
||||||
src={data}
|
src={data.thumbnailFileUrl}
|
||||||
alt={` ${data.id}`}
|
alt={`File ID: ${data.id}`}
|
||||||
/>
|
/>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
|
|
@ -432,112 +549,186 @@ export default function DetailImage() {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="mt-5">
|
|
||||||
<Label className="text-xl text-black">Berikan Komentar</Label>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<Avatar>
|
|
||||||
<AvatarImage
|
|
||||||
src="/images/avatar/avatar-1.png"
|
|
||||||
alt="@shadcn"
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
|
|
||||||
<textarea
|
|
||||||
className="flex-grow p-2 border rounded-lg focus:outline-none focus:border-yellow-400"
|
|
||||||
placeholder="Tuliskan komentar Anda di sini.."
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center mt-2 gap-3">
|
|
||||||
<button className="flex items-center text-gray-600 hover:text-gray-800">
|
|
||||||
<PaperclipIcon className="w-5 h-5" />
|
|
||||||
Lampirkan
|
|
||||||
</button>
|
|
||||||
<button className="flex items-center text-gray-600 hover:text-gray-800">
|
|
||||||
<SmileIcon className="w-5 h-5" />
|
|
||||||
Emoticon
|
|
||||||
</button>
|
|
||||||
<button className="ml-auto px-4 py-1 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600">
|
|
||||||
Kirim
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label className="text-xl text-black">Komentar</Label>
|
<Label className="text-xl text-black">Komentar</Label>
|
||||||
{commentsData.map((comment) => (
|
<div className="mt-4 border p-4 rounded bg-gray-50">
|
||||||
<div
|
<Textarea
|
||||||
key={comment.id}
|
placeholder="Tulis tanggapan Anda di sini..."
|
||||||
className="flex items-start gap-3 mt-2"
|
value={message}
|
||||||
>
|
onChange={handleInputChange}
|
||||||
<Avatar>
|
/>
|
||||||
<AvatarImage
|
<div className="flex justify-end mt-3">
|
||||||
src={comment.avatar}
|
<Button
|
||||||
alt={`@${comment.username}`}
|
color="primary"
|
||||||
/>
|
onClick={() => postData()}
|
||||||
</Avatar>
|
type="button"
|
||||||
<div className="flex flex-col">
|
>
|
||||||
<span className="text-gray-700 font-semibold">
|
Kirim Komentar
|
||||||
{comment.username}
|
</Button>
|
||||||
</span>
|
</div>
|
||||||
<span className="text-gray-500 text-sm">
|
</div>
|
||||||
{comment.date}
|
{listData?.map((item: any) => (
|
||||||
</span>
|
<div key={item.id} className="flex flex-col gap-3 mt-2 ">
|
||||||
<p className="text-gray-800 mt-1">{comment.text}</p>
|
<div className="flex flex-row gap-3">
|
||||||
<div
|
<Avatar className="mt-2">
|
||||||
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
<AvatarImage
|
||||||
onClick={() => handleReply(comment.id)}
|
src={"/assets/avatar-profile.png"}
|
||||||
>
|
alt={`@${item.username}`}
|
||||||
<DotSquare className="w-4 h-4" />
|
/>
|
||||||
<span className="ml-1">Balas</span>
|
</Avatar>
|
||||||
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-700 font-semibold">
|
||||||
|
{item.messageFrom.fullname}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">
|
||||||
|
{formatDate(item.createdAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-800 mt-1">{item.message}</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
{/* <div
|
||||||
|
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
||||||
|
onClick={() => handleReply(item.id)}
|
||||||
|
>
|
||||||
|
<DotSquare className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Balas</span>
|
||||||
|
</div> */}
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
|
onClick={() => deleteData(item.id)}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Delete</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{comment.replies.length > 0 && (
|
</div>
|
||||||
<div className="ml-8 mt-2">
|
{replyingTo === item.id && (
|
||||||
{comment.replies.map((reply: any, index: any) => (
|
<div className="ml-10 mt-2">
|
||||||
<div
|
<textarea
|
||||||
key={index}
|
id={`input-comment-${item.id}`}
|
||||||
className="flex items-start gap-3 mt-1"
|
className="w-full p-2 border rounded"
|
||||||
>
|
placeholder="Masukkan tanggapan anda"
|
||||||
<Avatar>
|
/>
|
||||||
|
<button
|
||||||
|
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
|
||||||
|
onClick={() => sendReplyData(item.id)}
|
||||||
|
>
|
||||||
|
Kirim
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.children?.length > 0 && (
|
||||||
|
<div className="ml-10 mt-2 flex flex-col">
|
||||||
|
{item.children.map((child: any) => (
|
||||||
|
<div
|
||||||
|
key={child.id}
|
||||||
|
className="flex flex-col gap-3 mt-2"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-3">
|
||||||
|
<Avatar className="mt-2">
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
src="https://github.com/shadcn.png"
|
src={"/assets/avatar-profile.png"}
|
||||||
alt={`@${reply.username}`}
|
alt={`@${child.username}`}
|
||||||
/>
|
/>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
<span className="text-gray-700 font-semibold">
|
<div className="flex items-center justify-between">
|
||||||
{reply.username}
|
<span className="text-gray-700 font-semibold">
|
||||||
</span>
|
{item.messageFrom.fullname}
|
||||||
<span className="text-gray-500 text-sm">
|
</span>
|
||||||
{reply.date}
|
<span className="text-gray-500 text-sm">
|
||||||
</span>
|
{formatDate(item.createdAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<p className="text-gray-800 mt-1">
|
<p className="text-gray-800 mt-1">
|
||||||
{reply.text}
|
{child.message}
|
||||||
</p>
|
</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
{/* <div
|
||||||
|
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
||||||
|
onClick={() => handleReply(child.id)}
|
||||||
|
>
|
||||||
|
<DotSquare className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Balas</span>
|
||||||
|
</div> */}
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
|
onClick={() => deleteData(child.id)}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Delete</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
{replyingTo === child.id && (
|
||||||
</div>
|
<div className="ml-10 mt-2">
|
||||||
)}
|
<textarea
|
||||||
</div>
|
id={`input-comment-${child.id}`}
|
||||||
|
className="w-full p-2 border rounded"
|
||||||
|
placeholder="Masukkan tanggapan anda"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
|
||||||
|
onClick={() => sendReplyData(child.id)}
|
||||||
|
>
|
||||||
|
Kirim
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{child.children?.length > 0 && (
|
||||||
|
<div className="ml-10 mt-2 flex flex-col mb-3">
|
||||||
|
{child.children.map((child2: any) => (
|
||||||
|
<div
|
||||||
|
key={child2.id}
|
||||||
|
className="flex flex-col gap-3 mt-2"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-3 ">
|
||||||
|
<Avatar className="mt-2">
|
||||||
|
<AvatarImage
|
||||||
|
src={"/assets/avatar-profile.png"}
|
||||||
|
alt={`@${child2.username}`}
|
||||||
|
/>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-700 font-semibold">
|
||||||
|
{item.messageFrom.fullname}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">
|
||||||
|
{formatDate(item.createdAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-800 mt-1">
|
||||||
|
{child2.message}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
deleteData(child2.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-4 h-4" />
|
||||||
|
<span className="ml-1">
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{replyingTo !== null && (
|
|
||||||
<div className="mt-4">
|
|
||||||
<textarea
|
|
||||||
className="w-full p-2 border rounded-md"
|
|
||||||
rows={3}
|
|
||||||
placeholder="Tulis balasan..."
|
|
||||||
value={replyText}
|
|
||||||
onChange={(e) => setReplyText(e.target.value)}
|
|
||||||
></textarea>
|
|
||||||
<button
|
|
||||||
className="mt-2 bg-yellow-500 text-white px-4 py-2 rounded-md"
|
|
||||||
onClick={() => addReply(replyingTo)}
|
|
||||||
>
|
|
||||||
Kirim
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import {
|
||||||
CarouselNext,
|
CarouselNext,
|
||||||
CarouselPrevious,
|
CarouselPrevious,
|
||||||
} from "@/components/ui/carousel";
|
} from "@/components/ui/carousel";
|
||||||
import { getListContent } from "@/service/landing/landing";
|
import { listCuratedContent } from "@/service/curated-content/curated-content";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
formatDateToIndonesian,
|
formatDateToIndonesian,
|
||||||
generateLocalizedPath,
|
generateLocalizedPath,
|
||||||
|
|
@ -35,10 +36,12 @@ const ImageSliderPage = () => {
|
||||||
const [imageData, setImageData] = useState<any>();
|
const [imageData, setImageData] = useState<any>();
|
||||||
const [displayImage, setDisplayImage] = useState<any[]>([]);
|
const [displayImage, setDisplayImage] = useState<any[]>([]);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page]);
|
}, [page, limit, search]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (imageData?.length > 0) {
|
if (imageData?.length > 0) {
|
||||||
|
|
@ -49,12 +52,7 @@ const ImageSliderPage = () => {
|
||||||
}, [imageData]);
|
}, [imageData]);
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const response = await getListContent({
|
const response = await listCuratedContent(search, limit, page - 1, 1, "1");
|
||||||
page: page - 1,
|
|
||||||
size: 6,
|
|
||||||
sortBy: "createdAt",
|
|
||||||
contentTypeId: "1",
|
|
||||||
});
|
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
||||||
const data = response?.data?.data;
|
const data = response?.data?.data;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { Link } from "@/components/navigation";
|
import { Link } from "@/components/navigation";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { listCuratedContent } from "@/service/curated-content/curated-content";
|
||||||
import { getListContent } from "@/service/landing/landing";
|
import { getListContent } from "@/service/landing/landing";
|
||||||
import { formatDateToIndonesian } from "@/utils/globals";
|
import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
|
@ -11,10 +12,12 @@ const VideoSliderPage = () => {
|
||||||
const [allVideoData, setAllVideoData] = useState<any[]>([]);
|
const [allVideoData, setAllVideoData] = useState<any[]>([]);
|
||||||
const [displayVideos, setDisplayVideos] = useState<any[]>([]);
|
const [displayVideos, setDisplayVideos] = useState<any[]>([]);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
fetchData();
|
||||||
}, []);
|
}, [page, limit, search]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (allVideoData?.length > 0) {
|
if (allVideoData?.length > 0) {
|
||||||
|
|
@ -24,14 +27,13 @@ const VideoSliderPage = () => {
|
||||||
}
|
}
|
||||||
}, [allVideoData]);
|
}, [allVideoData]);
|
||||||
|
|
||||||
const initFetch = async () => {
|
const fetchData = async () => {
|
||||||
const response = await getListContent({
|
const response = await listCuratedContent(search, limit, page - 1, 1, "2");
|
||||||
page: page - 1,
|
console.log(response);
|
||||||
size: 12,
|
|
||||||
sortBy: "createdAt",
|
const data = response?.data?.data;
|
||||||
contentTypeId: "2",
|
const contentData = data?.content;
|
||||||
});
|
setAllVideoData(contentData);
|
||||||
setAllVideoData(response?.data?.data?.content || []);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const shuffleAndSetVideos = () => {
|
const shuffleAndSetVideos = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import NewContent from "@/components/landing-page/new-content";
|
import NewContent from "@/components/landing-page/new-content";
|
||||||
|
|
@ -10,7 +10,11 @@ import { BarWave } from "react-cssfx-loading";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
const DetailAudio = () => {
|
const DetailAudio = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -29,8 +33,17 @@ const DetailAudio = () => {
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -41,6 +54,8 @@ const DetailAudio = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailAudio", response);
|
console.log("detailAudio", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -216,6 +231,59 @@ const DetailAudio = () => {
|
||||||
{ label: "XS", value: "800 x 450 px" },
|
{ label: "XS", value: "800 x 450 px" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="min-h-screen px-4 md:px-24 py-4">
|
<div className="min-h-screen px-4 md:px-24 py-4">
|
||||||
|
|
@ -310,6 +378,39 @@ const DetailAudio = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,11 @@ import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
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";
|
||||||
|
|
||||||
const Galery = (props: any) => {
|
const Galery = (props: any) => {
|
||||||
const [profile, setProfile] = useState<any>();
|
const [profile, setProfile] = useState<any>();
|
||||||
|
|
@ -20,7 +24,6 @@ const Galery = (props: any) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
// const { id } = router.query;
|
|
||||||
const page: any = searchParams?.get("page");
|
const page: any = searchParams?.get("page");
|
||||||
const title = searchParams?.get("title");
|
const title = searchParams?.get("title");
|
||||||
const category = searchParams?.get("category");
|
const category = searchParams?.get("category");
|
||||||
|
|
@ -28,7 +31,6 @@ const Galery = (props: any) => {
|
||||||
const { isInstitute, instituteId } = props;
|
const { isInstitute, instituteId } = props;
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userRoleId = getCookiesDecrypt("urie");
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [totalContent, setTotalContent] = useState();
|
const [totalContent, setTotalContent] = useState();
|
||||||
const [categoryFilter] = useState([]);
|
const [categoryFilter] = useState([]);
|
||||||
const [formatFilter] = useState([]);
|
const [formatFilter] = useState([]);
|
||||||
|
|
@ -42,6 +44,10 @@ const Galery = (props: any) => {
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
const [, setCopySuccess] = useState("");
|
const [, setCopySuccess] = useState("");
|
||||||
const [, setWishlistId] = useState();
|
const [, setWishlistId] = useState();
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDataVideo();
|
getDataVideo();
|
||||||
|
|
@ -219,8 +225,44 @@ const Galery = (props: any) => {
|
||||||
// toast.success("Link Berhasil Di Copy");
|
// toast.success("Link Berhasil Di Copy");
|
||||||
};
|
};
|
||||||
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [hasMounted, setHasMounted] = useState(false);
|
||||||
// Hooks
|
// Hooks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHasMounted(true);
|
setHasMounted(true);
|
||||||
|
|
@ -236,10 +278,10 @@ const Galery = (props: any) => {
|
||||||
<SidebarManagement />
|
<SidebarManagement />
|
||||||
<div className="w-2/3 p-12">
|
<div className="w-2/3 p-12">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold">Galeri Saya</h1>
|
<h1 className="text-2xl font-semibold mb-3">Galeri Saya</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col p-4">
|
<div className="flex flex-col">
|
||||||
<div className="mx-auto w-full max-w-7xl justify-start flex px-5 flex-col lg:flex-row gap-5 mb-4">
|
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col lg:flex-row gap-5 mb-4">
|
||||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|
@ -272,17 +314,54 @@ const Galery = (props: any) => {
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-0 lg:px-10">
|
<div className="">
|
||||||
{selectedTab == "video" ? (
|
{selectedTab == "video" ? (
|
||||||
contentVideo?.length > 0 ? (
|
contentVideo?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentVideo?.map((video: any) => (
|
{contentVideo?.map((video: any) => (
|
||||||
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={video?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
<div>
|
||||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg " />
|
||||||
</Link>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||||
|
</Link>
|
||||||
|
<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" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<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">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(video?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
@ -296,11 +375,7 @@ const Galery = (props: any) => {
|
||||||
contentAudio?.length > 0 ? (
|
contentAudio?.length > 0 ? (
|
||||||
<div className=" grid grid-cols-1 gap-6 ">
|
<div className=" grid grid-cols-1 gap-6 ">
|
||||||
{contentAudio?.map((audio: any) => (
|
{contentAudio?.map((audio: any) => (
|
||||||
<Link
|
<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">
|
||||||
href={`/audio/detail/${audio?.mediaUpload?.slug}`}
|
|
||||||
key={audio?.id}
|
|
||||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 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">
|
<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
|
<path
|
||||||
|
|
@ -309,8 +384,10 @@ const Galery = (props: any) => {
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col flex-1">
|
<div className="flex flex-col object-cover flex-1">
|
||||||
<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>
|
||||||
<div className="flex items-center justify-center gap-3">
|
<div className="flex items-center justify-center gap-3">
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
|
|
@ -327,7 +404,42 @@ const Galery = (props: any) => {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
<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" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<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">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(audio?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -339,12 +451,47 @@ const Galery = (props: any) => {
|
||||||
contentImage?.length > 0 ? (
|
contentImage?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentImage?.map((image: any) => (
|
{contentImage?.map((image: any) => (
|
||||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={image?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full h-full rounded-lg p-0">
|
||||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
<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" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<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">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(image?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
@ -357,7 +504,7 @@ const Galery = (props: any) => {
|
||||||
) : contentDocument.length > 0 ? (
|
) : contentDocument.length > 0 ? (
|
||||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{contentDocument?.map((document: any) => (
|
{contentDocument?.map((document: any) => (
|
||||||
<Link href={`/document/detail/${document?.mediaUpload?.slug}`} 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">
|
<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
|
<path
|
||||||
|
|
@ -368,7 +515,9 @@ const Galery = (props: any) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col flex-1 gap-2">
|
<div className="flex flex-col flex-1 gap-2">
|
||||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.mediaUpload?.title}</div>
|
<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">
|
<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">
|
<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" />
|
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||||
|
|
@ -376,7 +525,42 @@ const Galery = (props: any) => {
|
||||||
Download Dokumen
|
Download Dokumen
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
<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" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<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">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(document?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,12 @@ import withReactContent from "sweetalert2-react-content";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const Galery = (props: any) => {
|
const Galery = (props: any) => {
|
||||||
const [profile, setProfile] = useState<any>();
|
const [profile, setProfile] = useState<any>();
|
||||||
|
|
@ -23,7 +26,6 @@ const Galery = (props: any) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
// const { id } = router.query;
|
|
||||||
const page: any = searchParams?.get("page");
|
const page: any = searchParams?.get("page");
|
||||||
const title = searchParams?.get("title");
|
const title = searchParams?.get("title");
|
||||||
const category = searchParams?.get("category");
|
const category = searchParams?.get("category");
|
||||||
|
|
@ -45,6 +47,10 @@ const Galery = (props: any) => {
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
const [, setCopySuccess] = useState("");
|
const [, setCopySuccess] = useState("");
|
||||||
const [, setWishlistId] = useState();
|
const [, setWishlistId] = useState();
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDataVideo();
|
getDataVideo();
|
||||||
|
|
@ -225,8 +231,44 @@ const Galery = (props: any) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [hasMounted, setHasMounted] = useState(false);
|
||||||
// Hooks
|
// Hooks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHasMounted(true);
|
setHasMounted(true);
|
||||||
|
|
@ -244,8 +286,8 @@ const Galery = (props: any) => {
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col p-4">
|
<div className="flex flex-col mt-4">
|
||||||
<div className="mx-auto w-full max-w-7xl justify-start flex px-5 flex-col lg:flex-row gap-5 mb-4">
|
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col lg:flex-row gap-5 mb-4">
|
||||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|
@ -278,7 +320,7 @@ const Galery = (props: any) => {
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-0 lg:px-10">
|
<div className="px-2">
|
||||||
{selectedTab == "video" ? (
|
{selectedTab == "video" ? (
|
||||||
contentVideo?.length > 0 ? (
|
contentVideo?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
|
@ -289,37 +331,42 @@ const Galery = (props: any) => {
|
||||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<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>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(video?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
</DropdownMenuItem>
|
<p className="text-base items-center font-semibold mb-3">Bagikan</p>
|
||||||
<DropdownMenuItem>
|
</button>
|
||||||
<Link href={`/content-management/rewrite/create/${video?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
</PopoverTrigger>
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
<PopoverContent>
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
<div className="flex flex-col">
|
||||||
</Link>
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
</DropdownMenuItem>
|
<div className="flex flex-col mb-2">
|
||||||
<DropdownMenuItem>
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
<button onClick={() => copyToClip(video.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
</div>
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
Kirim
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</PopoverContent>
|
||||||
</DropdownMenuContent>
|
</Popover>
|
||||||
</DropdownMenu>
|
</div>
|
||||||
|
<a onClick={() => handleDelete(video?.id)} className="flex items-center gap-2 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -361,35 +408,41 @@ const Galery = (props: any) => {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<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>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(audio?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
</DropdownMenuItem>
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
<DropdownMenuItem>
|
</button>
|
||||||
<Link href={`/content-management/rewrite/create/${audio?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
</PopoverTrigger>
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
<PopoverContent>
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
<div className="flex flex-col">
|
||||||
</Link>
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
</DropdownMenuItem>
|
<div className="flex flex-col mb-2">
|
||||||
<DropdownMenuItem>
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
<button onClick={() => copyToClip(audio?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
</div>
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
Kirim
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</PopoverContent>
|
||||||
</DropdownMenuContent>
|
</Popover>
|
||||||
</DropdownMenu>
|
</div>
|
||||||
|
<a onClick={() => handleDelete(audio?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -402,41 +455,47 @@ const Galery = (props: any) => {
|
||||||
contentImage?.length > 0 ? (
|
contentImage?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentImage?.map((image: any) => (
|
{contentImage?.map((image: any) => (
|
||||||
<Card key={image?.id} className="">
|
<Card key={image?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||||
</Link>
|
</Link>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<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>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(image?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
</DropdownMenuItem>
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
<DropdownMenuItem>
|
</button>
|
||||||
<Link href={`/content-management/rewrite/create/${image?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
</PopoverTrigger>
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
<PopoverContent>
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
<div className="flex flex-col">
|
||||||
</Link>
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
</DropdownMenuItem>
|
<div className="flex flex-col mb-2">
|
||||||
<DropdownMenuItem>
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
<button onClick={() => copyToClip(image?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
</div>
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
Kirim
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</PopoverContent>
|
||||||
</DropdownMenuContent>
|
</Popover>
|
||||||
</DropdownMenu>
|
</div>
|
||||||
|
<a onClick={() => handleDelete(image?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
@ -470,35 +529,41 @@ const Galery = (props: any) => {
|
||||||
Download Dokumen
|
Download Dokumen
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<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>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(document?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
</DropdownMenuItem>
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
<DropdownMenuItem>
|
</button>
|
||||||
<Link href={`/content-management/rewrite/create/${document?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
</PopoverTrigger>
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
<PopoverContent>
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
<div className="flex flex-col">
|
||||||
</Link>
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
</DropdownMenuItem>
|
<div className="flex flex-col mb-2">
|
||||||
<DropdownMenuItem>
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
<button onClick={() => copyToClip(document?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
</div>
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
Kirim
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</PopoverContent>
|
||||||
</DropdownMenuContent>
|
</Popover>
|
||||||
</DropdownMenu>
|
</div>
|
||||||
|
<a onClick={() => handleDelete(document?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const imageSchema = z.object({
|
||||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const page = (props: { states?: string }) => {
|
const page = (props: any) => {
|
||||||
const { states } = props;
|
const { states } = props;
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -255,7 +255,7 @@ const page = (props: { states?: string }) => {
|
||||||
const maxRetries = 20;
|
const maxRetries = 20;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const waitForStatusUpdate = async () => {
|
const waitForStatusUpdate: any = async () => {
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
const res = await getDetailArticle(id);
|
const res = await getDetailArticle(id);
|
||||||
const articleData = res?.data?.data;
|
const articleData = res?.data?.data;
|
||||||
|
|
@ -276,7 +276,7 @@ const page = (props: { states?: string }) => {
|
||||||
const articleImagesData = articleData?.imagesUrl?.split(",");
|
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||||
setValue("description", cleanArticleBody || "");
|
setValue("description", cleanArticleBody || "");
|
||||||
setArticleBody(cleanArticleBody || "");
|
setArticleBody(cleanArticleBody || "");
|
||||||
setDetailData(articleData);
|
setDetailArticle(articleData);
|
||||||
setSelectedArticleId(id);
|
setSelectedArticleId(id);
|
||||||
setArticleImages(articleImagesData || []);
|
setArticleImages(articleImagesData || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -404,7 +404,11 @@ const page = (props: { states?: string }) => {
|
||||||
{articleIds.map((id: any, index: any) => (
|
{articleIds.map((id: any, index: any) => (
|
||||||
<p
|
<p
|
||||||
key={index}
|
key={index}
|
||||||
className={`text-black m-1 ${selectedArticleId === id ? "bg-[#31ce36] cursor-pointer border border-[#31ce36]" : "bg-[#48abf7] cursor-pointer border border-[#48abf7]"}`}
|
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]"
|
||||||
|
}`}
|
||||||
onClick={() => handleArticleIdClick(id)}
|
onClick={() => handleArticleIdClick(id)}
|
||||||
>
|
>
|
||||||
{id}
|
{id}
|
||||||
|
|
@ -429,7 +433,7 @@ const page = (props: { states?: string }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{articleBody === null || articleBody === "" ? <div className="text-red-400 px-0 text-sm">Deskripsi tidak boleh kosong*</div> : ""}
|
{articleBody === null || articleBody === "" ? <p className="text-red-400 px-0 text-sm">Deskripsi tidak boleh kosong*</p> : ""}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -441,7 +445,7 @@ const page = (props: { states?: string }) => {
|
||||||
>
|
>
|
||||||
Kembali
|
Kembali
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" className="border border-blue-500 bg-blue-500 text-white">
|
<Button type="submit" className="border border-blue-500 bg-blue-500 text-white">
|
||||||
Simpan
|
Simpan
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { close, error, loading } from "@/config/swal";
|
||||||
import { useRouter } from "@/i18n/routing";
|
import { useRouter } from "@/i18n/routing";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { getContentRewrite, getInfoProfile } from "@/service/landing/landing";
|
import { getContentRewrite, getInfoProfile } from "@/service/landing/landing";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useParams, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
|
@ -15,15 +15,17 @@ import { generateDataArticle } from "@/service/content/ai";
|
||||||
import HeaderManagement from "@/components/landing-page/header-management";
|
import HeaderManagement from "@/components/landing-page/header-management";
|
||||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||||
import { saveContentRewrite } from "@/service/content/content";
|
import { saveContentRewrite } from "@/service/content/content";
|
||||||
|
import CustomEditor from "@/components/editor/custom-editor";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
const page = (props: any) => {
|
const page = () => {
|
||||||
const { states } = props;
|
|
||||||
const [profile, setProfile] = useState();
|
const [profile, setProfile] = useState();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [, setLoadingState] = useState(false);
|
const [, setLoadingState] = useState(false);
|
||||||
const id: any = searchParams?.get("title");
|
const getParams = useParams();
|
||||||
|
const id: any = getParams?.id;
|
||||||
const [content, setContent] = useState<any>([]);
|
const [content, setContent] = useState<any>([]);
|
||||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||||
const [listSuggestion, setListSuggestion] = useState();
|
const [listSuggestion, setListSuggestion] = useState();
|
||||||
|
|
@ -199,96 +201,95 @@ const page = (props: any) => {
|
||||||
<div className="text-xl font-bold mb-5">Detail Content Rewrite</div>
|
<div className="text-xl font-bold mb-5">Detail Content Rewrite</div>
|
||||||
<div className="p-8 border border-black rounded-lg">
|
<div className="p-8 border border-black rounded-lg">
|
||||||
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
||||||
{/* {content && ( */}
|
{content && (
|
||||||
<>
|
<>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="mb-3">
|
<div className="mb-1">
|
||||||
<p className="font-semibold">Judul</p>
|
<p className="font-bold text-base">Judul</p>
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={`w-full p-2 border rounded-md mb-3 border-black ${errors.title ? "is-invalid" : ""}`}
|
|
||||||
{...register("title", {
|
|
||||||
value: content?.title,
|
|
||||||
})}
|
|
||||||
id="title"
|
|
||||||
defaultValue={content?.title}
|
|
||||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col justify-between lg:flex-row">
|
|
||||||
<div className="w-50%">
|
|
||||||
<div className="mb-3">
|
|
||||||
<p className="font-semibold">Main Keyword</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<Input
|
||||||
<input type="text" className="border mb-3 w-full rounded-md p-2 border-black" id="mainKeyword" name="mainKeyword" placeholder="Masukan Main Keyword disini!" defaultValue={content?.mainKeyword} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-50%">
|
|
||||||
<div className="mb-3">
|
|
||||||
<label htmlFor="title" className="font-semibold">
|
|
||||||
Additional Keyword{" "}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input
|
|
||||||
className="border mb-3 rounded-md p-2 border-black"
|
|
||||||
type="text"
|
type="text"
|
||||||
id="additionalKeyword"
|
className={`w-full mb-3 ${errors.title ? "is-invalid" : ""}`}
|
||||||
name="additionalKeyword"
|
{...register("title", {
|
||||||
// onChange={(e) =>
|
value: content?.title,
|
||||||
// setSelectedMainKeyword(e.target.value)
|
})}
|
||||||
// }
|
id="title"
|
||||||
placeholder="Masukan Additional Keyword disini!"
|
defaultValue={content?.title}
|
||||||
defaultValue={content?.additionalKeyword}
|
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex justify-between gap-3 lg:flex-row">
|
||||||
|
<div className="w-1/2">
|
||||||
|
<div className="mb-1">
|
||||||
|
<p className="font-semibold">Main Keyword</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Input type="text" className="border mb-3 w-full rounded-md p-2 border-black" id="mainKeyword" name="mainKeyword" placeholder="Masukan Main Keyword disini!" defaultValue={content?.mainKeyword} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-1/2">
|
||||||
|
<div className="mb-1">
|
||||||
|
<label htmlFor="title" className="font-semibold">
|
||||||
|
Additional Keyword{" "}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
className="border mb-3 rounded-md p-2 border-black"
|
||||||
|
type="text"
|
||||||
|
id="additionalKeyword"
|
||||||
|
name="additionalKeyword"
|
||||||
|
// onChange={(e) =>
|
||||||
|
// setSelectedMainKeyword(e.target.value)
|
||||||
|
// }
|
||||||
|
placeholder="Masukan Additional Keyword disini!"
|
||||||
|
defaultValue={content?.additionalKeyword}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col justify-between lg:flex-row">
|
<div className="flex gap-3 justify-between lg:flex-row">
|
||||||
<div className="w-50%">
|
<div className="w-1/2">
|
||||||
|
<div className="font-semibold mb-1">
|
||||||
|
<label htmlFor="metaTitle">Meta Title</label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
className="border rounded-md mb-3 p-2 border-black"
|
||||||
|
id="metaTitle"
|
||||||
|
name="metaTitle"
|
||||||
|
// onChange={(e) =>
|
||||||
|
// setSelectedMainKeyword(e.target.value)
|
||||||
|
// }
|
||||||
|
placeholder="Masukan Meta Title disini!"
|
||||||
|
defaultValue={content?.metaTitle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-1/2">
|
||||||
|
<div className="font-semibold mb-1">
|
||||||
|
<label htmlFor="metaDescription">Meta Description</label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
className="border rounded-md mb-3 p-2 border-black"
|
||||||
|
id="metaDescription"
|
||||||
|
name="metaDescription"
|
||||||
|
// onChange={(e) =>
|
||||||
|
// setSelectedMainKeyword(e.target.value)
|
||||||
|
// }
|
||||||
|
placeholder="Masukan Meta Description disini!"
|
||||||
|
defaultValue={content?.metaDescription}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
<div className="font-semibold mb-3">
|
<div className="font-semibold mb-3">
|
||||||
<label htmlFor="metaTitle">Meta Title</label>
|
<label htmlFor="description">Deskripsi Artikel</label>
|
||||||
|
<CustomEditor onChange={(e: any) => {}} initialData={articleBody || ""} />
|
||||||
</div>
|
</div>
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="border rounded-md mb-3 p-2 border-black"
|
|
||||||
id="metaTitle"
|
|
||||||
name="metaTitle"
|
|
||||||
// onChange={(e) =>
|
|
||||||
// setSelectedMainKeyword(e.target.value)
|
|
||||||
// }
|
|
||||||
placeholder="Masukan Meta Title disini!"
|
|
||||||
defaultValue={content?.metaTitle}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-50%">
|
</>
|
||||||
<div className="font-semibold mb-3">
|
)}
|
||||||
<label htmlFor="metaDescription">Meta Description</label>
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="border rounded-md mb-3 p-2 border-black"
|
|
||||||
id="metaDescription"
|
|
||||||
name="metaDescription"
|
|
||||||
// onChange={(e) =>
|
|
||||||
// setSelectedMainKeyword(e.target.value)
|
|
||||||
// }
|
|
||||||
placeholder="Masukan Meta Description disini!"
|
|
||||||
defaultValue={content?.metaDescription}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<div className="font-semibold mb-3">
|
|
||||||
<label htmlFor="description">Deskripsi Artikel</label>
|
|
||||||
</div>
|
|
||||||
{articleBody === null || articleBody === "" ? <div className="w-full px-0 text-sm">Deskripsi tidak boleh kosong</div> : ""}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
{/* )} */}
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,13 @@ import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const page = (props: any) => {
|
const page = () => {
|
||||||
const [, setProfile] = useState();
|
const [, setProfile] = useState();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
@ -21,23 +26,21 @@ const page = (props: any) => {
|
||||||
const title = searchParams?.get("title");
|
const title = searchParams?.get("title");
|
||||||
const category = searchParams?.get("category");
|
const category = searchParams?.get("category");
|
||||||
const pages = page ? page - 1 : 0;
|
const pages = page ? page - 1 : 0;
|
||||||
|
|
||||||
const { isInstitute, instituteId } = props;
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userRoleId = getCookiesDecrypt("urie");
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [, setGetTotalPage] = useState();
|
const [, setGetTotalPage] = useState();
|
||||||
const [totalContent, setTotalContent] = useState<any>();
|
const [totalContent, setTotalContent] = useState<any>();
|
||||||
const [contentImage, setContentImage] = useState([]);
|
const [contentImage, setContentImage] = useState([]);
|
||||||
|
|
||||||
const [categoryFilter] = useState([]);
|
const [categoryFilter] = useState([]);
|
||||||
const [formatFilter] = useState([]);
|
const [formatFilter] = useState([]);
|
||||||
const [sortBy] = useState();
|
const [sortBy] = useState();
|
||||||
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
const [, setCopySuccess] = useState("");
|
const [, setCopySuccess] = useState("");
|
||||||
|
|
||||||
const [, setWishlistId] = useState();
|
const [, setWishlistId] = useState();
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
|
|
@ -153,6 +156,43 @@ const page = (props: any) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeaderManagement />
|
<HeaderManagement />
|
||||||
|
|
@ -162,16 +202,53 @@ const page = (props: any) => {
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold mb-4">Galeri Content Rewrite</h1>
|
<h1 className="text-2xl font-semibold mb-4">Galeri Content Rewrite</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-0 lg:px-10">
|
<div className="">
|
||||||
{contentImage?.length > 0 ? (
|
{contentImage?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentImage?.map((image: any) => (
|
{contentImage?.map((image: any) => (
|
||||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={image?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||||
<Link href={`/content-management/rewrite/detail/${image.id}`}>
|
<div className="">
|
||||||
<img src={image?.thumbnailUrl} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<Link href={`/content-management/rewrite/detail/${image.id}`}>
|
||||||
|
<img src={image?.thumbnailUrl} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
|
</Link>
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.title}</div>
|
||||||
</Link>
|
<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" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<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">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(image?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import NewContent from "@/components/landing-page/new-content";
|
import NewContent from "@/components/landing-page/new-content";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
const DetailDocument = () => {
|
const DetailDocument = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -29,8 +33,17 @@ const DetailDocument = () => {
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -41,6 +54,8 @@ const DetailDocument = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailDocument", response);
|
console.log("detailDocument", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -216,6 +231,59 @@ const DetailDocument = () => {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="px-4 md:px-24 py-4">
|
<div className="px-4 md:px-24 py-4">
|
||||||
|
|
@ -307,6 +375,39 @@ const DetailDocument = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import NewContent from "@/components/landing-page/new-content";
|
import NewContent from "@/components/landing-page/new-content";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const DetailInfo = () => {
|
const DetailInfo = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -29,8 +33,17 @@ const DetailInfo = () => {
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -41,6 +54,8 @@ const DetailInfo = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailImage", response);
|
console.log("detailImage", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -219,6 +234,59 @@ const DetailInfo = () => {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="min-h-screen px-4 md:px-24 py-4">
|
<div className="min-h-screen px-4 md:px-24 py-4">
|
||||||
|
|
@ -296,7 +364,7 @@ const DetailInfo = () => {
|
||||||
{sizes.map((size: any) => (
|
{sizes.map((size: any) => (
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
|
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
|
||||||
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
|
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={(e) => setImageSizeSelected(e.target.value)} className="text-red-600 focus:ring-red-600" />
|
||||||
<div className="text-sm">{size.label}</div>
|
<div className="text-sm">{size.label}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
|
|
@ -321,6 +389,39 @@ const DetailInfo = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ const FilterPage = () => {
|
||||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||||
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
||||||
<Link href={`/image/detail/${image?.slug}`}>
|
<Link href={`/image/detail/${image?.slug}`}>
|
||||||
<img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" />
|
||||||
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
|
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
|
||||||
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
||||||
{image?.clickCount}{" "}
|
{image?.clickCount}{" "}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import LayoutProvider from "@/providers/layout.provider";
|
||||||
|
import LayoutContentProvider from "@/providers/content.provider";
|
||||||
|
import DashCodeSidebar from "@/components/partials/sidebar";
|
||||||
|
import DashCodeFooter from "@/components/partials/footer";
|
||||||
|
import ThemeCustomize from "@/components/partials/customizer";
|
||||||
|
import DashCodeHeader from "@/components/partials/header";
|
||||||
|
|
||||||
|
import { redirect } from "@/components/navigation";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
|
||||||
|
const layout = async ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Navbar />
|
||||||
|
{children}
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default layout;
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Link, usePathname } from "@/i18n/routing";
|
||||||
|
import { getUserNotifications } from "@/service/landing/landing";
|
||||||
|
import { getTimestamp } from "@/utils/globals";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const InboxSection = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
const isUpdate = pathname.includes("update");
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const page: any = searchParams?.get("page");
|
||||||
|
|
||||||
|
const pages = page ? page - 1 : 0;
|
||||||
|
|
||||||
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
const [getTotalData, setGetTotalData] = useState();
|
||||||
|
const [, setGetTotalPage] = useState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getNotif() {
|
||||||
|
const response = await getUserNotifications(pages, 2);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotifUpdate() {
|
||||||
|
const response = await getUserNotifications(pages, 3);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdate) {
|
||||||
|
getNotifUpdate();
|
||||||
|
} else {
|
||||||
|
getNotif();
|
||||||
|
}
|
||||||
|
}, [pages]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl h-screen flex flex-col mx-auto p-4 lg:p-24 gap-5">
|
||||||
|
<div className="flex items-center justify-center mb-6">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="60px" height="60px" viewBox="0 0 24 24">
|
||||||
|
<g fill="#bb3523" fill-rule="evenodd" clip-rule="evenodd">
|
||||||
|
<path d="M16 9a4 4 0 1 1-8 0a4 4 0 0 1 8 0m-2 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
|
||||||
|
<path d="M12 1C5.925 1 1 5.925 1 12s4.925 11 11 11s11-4.925 11-11S18.075 1 12 1M3 12c0 2.09.713 4.014 1.908 5.542A8.99 8.99 0 0 1 12.065 14a8.98 8.98 0 0 1 7.092 3.458A9 9 0 1 0 3 12m9 9a8.96 8.96 0 0 1-5.672-2.012A6.99 6.99 0 0 1 12.065 16a6.99 6.99 0 0 1 5.689 2.92A8.96 8.96 0 0 1 12 21" />
|
||||||
|
</g>
|
||||||
|
</svg>{" "}
|
||||||
|
<h2 className="ml-4 text-[15px] lg:text-[32px] font-semibold text-gray-800">Pesan Masuk</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-center items-center gap-3">
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="flex flex-row gap-10 items-center justify-center">
|
||||||
|
<div>
|
||||||
|
<p className="bg-[#bb3523] py-1 px-3 rounded-full">Pesan Masuk</p>
|
||||||
|
</div>
|
||||||
|
<Link href={`/inbox/update`}>Update</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="py-10 px-8 w-[400px] mt-3 border border-black rounded-lg flex flex-col">
|
||||||
|
<h1 className="mb-3 text-lg font-semibold">List Notifikasi</h1>
|
||||||
|
<div className="hover:bg-slate-200 rounded-md">
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="flex flex-row items-center ml-1" href={"/" + list.redirectUrl}>
|
||||||
|
{(() => {
|
||||||
|
switch (Number(list.notificationTypeId)) {
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comment" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:upload" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="la:check-double" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comments" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:tasks" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:pencil-square" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:times-circle" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:info-circle" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block">{list.message}</span>
|
||||||
|
<span className="text-xs">{getTimestamp(new Date(list.createdAt))}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InboxSection;
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Link, usePathname } from "@/i18n/routing";
|
||||||
|
import { getUserNotifications } from "@/service/landing/landing";
|
||||||
|
import { getTimestamp } from "@/utils/globals";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const UpdateSection = () => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const isUpdate = pathname.includes("update");
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const page: any = searchParams?.get("page");
|
||||||
|
const pages = page ? page - 1 : 0;
|
||||||
|
|
||||||
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
const [getTotalData, setGetTotalData] = useState();
|
||||||
|
const [, setGetTotalPage] = useState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getNotif() {
|
||||||
|
const response = await getUserNotifications(pages, 2);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotifUpdate() {
|
||||||
|
const response = await getUserNotifications(pages, 3);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdate) {
|
||||||
|
getNotifUpdate();
|
||||||
|
} else {
|
||||||
|
getNotif();
|
||||||
|
}
|
||||||
|
}, [pages]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl h-screen flex flex-col mx-auto p-4 lg:p-24 gap-5">
|
||||||
|
<div className="flex items-center justify-center mb-6">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="60px" height="60px" viewBox="0 0 24 24">
|
||||||
|
<g fill="#bb3523" fill-rule="evenodd" clip-rule="evenodd">
|
||||||
|
<path d="M16 9a4 4 0 1 1-8 0a4 4 0 0 1 8 0m-2 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
|
||||||
|
<path d="M12 1C5.925 1 1 5.925 1 12s4.925 11 11 11s11-4.925 11-11S18.075 1 12 1M3 12c0 2.09.713 4.014 1.908 5.542A8.99 8.99 0 0 1 12.065 14a8.98 8.98 0 0 1 7.092 3.458A9 9 0 1 0 3 12m9 9a8.96 8.96 0 0 1-5.672-2.012A6.99 6.99 0 0 1 12.065 16a6.99 6.99 0 0 1 5.689 2.92A8.96 8.96 0 0 1 12 21" />
|
||||||
|
</g>
|
||||||
|
</svg>{" "}
|
||||||
|
<h2 className="ml-4 text-[15px] lg:text-[32px] font-semibold text-gray-800">Update</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-center gap-3">
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="flex flex-row gap-10 items-center justify-center">
|
||||||
|
<div>
|
||||||
|
<Link href={`/inbox`}>Pesan Masuk</Link>
|
||||||
|
</div>
|
||||||
|
<div className="bg-[#bb3523] py-1 px-3 rounded-full">Update</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="py-10 px-8 w-[400px] mt-3 border border-black rounded-lg flex flex-col">
|
||||||
|
<h1 className="mb-3 text-lg font-semibold">List Notifikasi</h1>
|
||||||
|
<div className="hover:bg-slate-200 rounded-md">
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="flex flex-row items-center ml-1" href={"/" + list.redirectUrl}>
|
||||||
|
{(() => {
|
||||||
|
switch (Number(list.notificationTypeId)) {
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comment" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:upload" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="la:check-double" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comments" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return (
|
||||||
|
<div className="notif-icon notif-danger">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:tasks" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
return (
|
||||||
|
<div className="notif-icon notif-primary">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:pencil-square" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:times-circle" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:info-circle" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block">{list.message}</span>
|
||||||
|
<span className="text-xs">{getTimestamp(new Date(list.createdAt))}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpdateSection;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
|
|
@ -9,8 +9,13 @@ import NewContent from "@/components/landing-page/new-content";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
|
||||||
const DetailVideo = () => {
|
const DetailVideo = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -28,8 +33,17 @@ const DetailVideo = () => {
|
||||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -40,6 +54,8 @@ const DetailVideo = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailVideo", response);
|
console.log("detailVideo", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -216,6 +232,59 @@ const DetailVideo = () => {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="px-4 md:px-24 py-4">
|
<div className="px-4 md:px-24 py-4">
|
||||||
|
|
@ -318,6 +387,39 @@ const DetailVideo = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
||||||
<div className="flex w-full items-center overflow-hidden min-h-dvh h-dvh basis-full">
|
<div className="flex w-full items-center overflow-hidden min-h-dvh h-dvh basis-full">
|
||||||
<div className="overflow-y-auto flex flex-wrap w-full h-dvh">
|
<div className="overflow-y-auto flex flex-wrap w-full h-dvh">
|
||||||
<div
|
<div
|
||||||
className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600
|
className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600 relative z-[1] bg-default-50"
|
||||||
relative z-[1] bg-default-50"
|
|
||||||
>
|
>
|
||||||
<div className="max-w-[520px] pt-16 ps-20 ">
|
<div className="max-w-[520px] pt-16 ps-20 ">
|
||||||
<Link href="/" className="mb-6 inline-block">
|
<Link href="/" className="mb-6 inline-block">
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -55,7 +55,8 @@ import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import ReactAudioPlayer from "react-audio-player";
|
import WavesurferPlayer from "@wavesurfer/react";
|
||||||
|
import WaveSurfer from "wavesurfer.js";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -140,6 +141,23 @@ export default function FormAudioDetail() {
|
||||||
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
||||||
const [audioPlaying, setAudioPlaying] = useState<any>(null);
|
const [audioPlaying, setAudioPlaying] = useState<any>(null);
|
||||||
|
|
||||||
|
const waveSurferRef = useRef<any>(null);
|
||||||
|
|
||||||
|
|
||||||
|
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
|
const onReady = (ws: any) => {
|
||||||
|
setWavesurfer(ws)
|
||||||
|
setIsPlaying(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPlayPause = () => {
|
||||||
|
wavesurfer && wavesurfer.playPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let fileTypeId = "4";
|
let fileTypeId = "4";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -245,9 +263,17 @@ export default function FormAudioDetail() {
|
||||||
setSelectedTarget(details.categoryId); // Untuk dropdown
|
setSelectedTarget(details.categoryId); // Untuk dropdown
|
||||||
|
|
||||||
const filesData = details.files || [];
|
const filesData = details.files || [];
|
||||||
const fileUrls = filesData.map((file: { secondaryUrl: string }) =>
|
const audioFiles = filesData.filter(
|
||||||
file.secondaryUrl ? file.secondaryUrl : "default-image.jpg"
|
(file: any) =>
|
||||||
|
file.contentType && file.contentType.startsWith("video/webm")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const fileUrls = audioFiles.map((file: { secondaryUrl: string }) =>
|
||||||
|
file.secondaryUrl ? file.secondaryUrl : "default-audio.mp3"
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("audio", fileUrls);
|
||||||
|
|
||||||
setDetailThumb(fileUrls);
|
setDetailThumb(fileUrls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,60 +456,21 @@ export default function FormAudioDetail() {
|
||||||
|
|
||||||
<Label className="text-xl text-black">File Mediaaa</Label>
|
<Label className="text-xl text-black">File Mediaaa</Label>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Swiper
|
<div className={"container example"}>
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
{detailThumb?.map((url: any, index: number) => (
|
||||||
modules={[FreeMode, Navigation, Thumbs]}
|
<div key={url.id}>
|
||||||
navigation={false}
|
<WavesurferPlayer
|
||||||
className="w-full"
|
height={500}
|
||||||
>
|
waveColor="red"
|
||||||
{detailThumb?.map((data: any) => {
|
url={url}
|
||||||
return (
|
onReady={onReady}
|
||||||
<SwiperSlide key={data.id}>
|
onPlay={() => setIsPlaying(true)}
|
||||||
<div className="relative">
|
onPause={() => setIsPlaying(false)}
|
||||||
<ReactAudioPlayer src={data} autoPlay controls />
|
/>
|
||||||
{/* <button
|
</div>
|
||||||
className="absolute bottom-2 left-2 text-white bg-black p-1 rounded"
|
))}
|
||||||
onClick={() => handleAudioPlayPause(data)}
|
<p onClick={onPlayPause}>{isPlaying ? "Pause" : "Play"}</p>
|
||||||
>
|
</div>
|
||||||
{audioPlaying === data ? "Pause" : "Play"}
|
|
||||||
</button> */}
|
|
||||||
</div>
|
|
||||||
</SwiperSlide>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Swiper>
|
|
||||||
|
|
||||||
{/* <div className="mt-2">
|
|
||||||
<Swiper
|
|
||||||
onSwiper={setThumbsSwiper}
|
|
||||||
slidesPerView={6}
|
|
||||||
spaceBetween={8}
|
|
||||||
pagination={{
|
|
||||||
clickable: true,
|
|
||||||
}}
|
|
||||||
modules={[Pagination, Thumbs]}
|
|
||||||
>
|
|
||||||
{detailThumb?.map((data: any) => {
|
|
||||||
return (
|
|
||||||
<SwiperSlide key={data.id}>
|
|
||||||
<div className="relative">
|
|
||||||
<ReactAudioPlayer src={data} autoPlay controls />
|
|
||||||
<button
|
|
||||||
className="absolute bottom-2 left-2 text-white bg-black p-1 rounded"
|
|
||||||
onClick={() =>
|
|
||||||
handleAudioPlayPause(data.secondaryUrl)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{audioPlaying === data.secondaryUrl
|
|
||||||
? "Pause"
|
|
||||||
: "Play"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</SwiperSlide>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Swiper>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ export default function FormImageDetail() {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupPlacement = (
|
const setupPlacement = (
|
||||||
index: number,
|
index: number,
|
||||||
placement: string,
|
placement: string,
|
||||||
|
|
@ -679,7 +679,7 @@ export default function FormImageDetail() {
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{isUserMabesApprover &&
|
{isUserMabesApprover && (
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -756,7 +756,7 @@ export default function FormImageDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,17 @@ type Option = {
|
||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface FileData {
|
||||||
|
contentId: number;
|
||||||
|
placement?: string[];
|
||||||
|
[key: string]: any; // Extendable for additional properties
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlacementData {
|
||||||
|
mediaFileId: number;
|
||||||
|
placements: string;
|
||||||
|
}
|
||||||
|
|
||||||
const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
() => {
|
() => {
|
||||||
return import("@/components/editor/custom-editor");
|
return import("@/components/editor/custom-editor");
|
||||||
|
|
@ -118,7 +129,6 @@ export default function FormConvertSPIT() {
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
|
||||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||||
const [articleBody, setArticleBody] = useState<string>("");
|
const [articleBody, setArticleBody] = useState<string>("");
|
||||||
|
|
@ -128,7 +138,10 @@ export default function FormConvertSPIT() {
|
||||||
const [detailData, setDetailData] = useState<any>(null);
|
const [detailData, setDetailData] = useState<any>(null);
|
||||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||||
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
|
const userLevelNumber = getCookiesDecrypt("ulne");
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [unitSelection, setUnitSelection] = useState({
|
const [unitSelection, setUnitSelection] = useState({
|
||||||
allUnit: false,
|
allUnit: false,
|
||||||
|
|
@ -137,6 +150,7 @@ export default function FormConvertSPIT() {
|
||||||
polres: false,
|
polres: false,
|
||||||
});
|
});
|
||||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||||
|
const [placementLength, setPlacementLength] = useState([]);
|
||||||
|
|
||||||
const options: Option[] = [
|
const options: Option[] = [
|
||||||
{ id: "all", label: "SEMUA" },
|
{ id: "all", label: "SEMUA" },
|
||||||
|
|
@ -187,6 +201,17 @@ export default function FormConvertSPIT() {
|
||||||
initState();
|
initState();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
userLevelId != undefined &&
|
||||||
|
roleId != undefined &&
|
||||||
|
userLevelId == "216" &&
|
||||||
|
roleId == "3"
|
||||||
|
) {
|
||||||
|
setIsMabesApprover(true);
|
||||||
|
}
|
||||||
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
const getCategories = async () => {
|
const getCategories = async () => {
|
||||||
try {
|
try {
|
||||||
const category = await listCategory(fileTypeId);
|
const category = await listCategory(fileTypeId);
|
||||||
|
|
@ -247,26 +272,64 @@ export default function FormConvertSPIT() {
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCheckboxChangeFile = (mediaFileId: any, value: any) => {
|
const getPlacement = (): PlacementData[] => {
|
||||||
setTempFile((prev: any) =>
|
return tempFile
|
||||||
prev.map((file: any) =>
|
.filter((file: FileData) => (file.placement || []).length > 0) // Gunakan default array
|
||||||
file.contentId === mediaFileId
|
.map((file: FileData) => ({
|
||||||
? {
|
mediaFileId: Number(file.contentId),
|
||||||
...file,
|
placements: (file.placement || []).join(","), // Gunakan default array
|
||||||
placement: file.placement.includes(value)
|
}));
|
||||||
? file.placement.filter((item: any) => item !== value) // Hapus jika sudah ada
|
|
||||||
: [...file.placement, value], // Tambah jika belum ada
|
|
||||||
}
|
|
||||||
: file
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPlacement = () => {
|
const setupPlacement = (value: string, id: number): void => {
|
||||||
return tempFile.map((file: any) => ({
|
const updatedFiles = tempFile.map((file: FileData) => {
|
||||||
mediaFileId: Number(file.contentId),
|
if (file.contentId === id) {
|
||||||
placements: file.placement.join(","),
|
const currentPlacement = file.placement || [];
|
||||||
}));
|
if (currentPlacement.includes(value)) {
|
||||||
|
// Remove the placement value
|
||||||
|
file.placement = currentPlacement.filter((val) => val !== value);
|
||||||
|
} else {
|
||||||
|
// Add the placement value
|
||||||
|
file.placement =
|
||||||
|
value === "all"
|
||||||
|
? ["all", "mabes", "polda", "international"]
|
||||||
|
: [...currentPlacement, value];
|
||||||
|
if (file.placement.includes("all") && value !== "all") {
|
||||||
|
file.placement = file.placement.filter((val) => val !== "all");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
|
||||||
|
const placementLength = updatedFiles.reduce(
|
||||||
|
(acc: any, file: any) => acc + (file.placement?.length || 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
setTempFile(
|
||||||
|
updatedFiles.sort((a: any, b: any) => a.contentId - b.contentId)
|
||||||
|
);
|
||||||
|
setPlacementLength(placementLength);
|
||||||
|
|
||||||
|
console.log("Updated Files:", updatedFiles);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCheckboxChangeFile = (contentId: number, value: string) => {
|
||||||
|
setTempFile((prevTempFile: any) => {
|
||||||
|
return prevTempFile.map((file: any) => {
|
||||||
|
if (file.contentId === contentId) {
|
||||||
|
const isChecked = file.placement?.includes(value);
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
placement: isChecked
|
||||||
|
? file.placement.filter((v: any) => v !== value) // Uncheck
|
||||||
|
: [...(file.placement || []), value], // Check
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCheckboxChange = (id: string): void => {
|
const handleCheckboxChange = (id: string): void => {
|
||||||
|
|
@ -296,11 +359,17 @@ export default function FormConvertSPIT() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const save = async (data: any) => {
|
const save = async (data: {
|
||||||
|
contentTitle: string;
|
||||||
|
contentDescription: string;
|
||||||
|
contentRewriteDescription: string;
|
||||||
|
contentCreator: string;
|
||||||
|
}): Promise<void> => {
|
||||||
const description =
|
const description =
|
||||||
selectedFileType === "original"
|
selectedFileType === "original"
|
||||||
? data.contentDescription
|
? data.contentDescription
|
||||||
: data.contentRewriteDescription;
|
: data.contentRewriteDescription;
|
||||||
|
|
||||||
const requestData = {
|
const requestData = {
|
||||||
spitId: id,
|
spitId: id,
|
||||||
title: data.contentTitle,
|
title: data.contentTitle,
|
||||||
|
|
@ -310,7 +379,7 @@ export default function FormConvertSPIT() {
|
||||||
categoryId: selectedCategoryId,
|
categoryId: selectedCategoryId,
|
||||||
publishedFor: publishedFor.join(","),
|
publishedFor: publishedFor.join(","),
|
||||||
creator: data.contentCreator,
|
creator: data.contentCreator,
|
||||||
files: getPlacement(),
|
files: getPlacement(), // Include placement data
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await convertSPIT(requestData);
|
const response = await convertSPIT(requestData);
|
||||||
|
|
@ -630,18 +699,29 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
{isMabesApprover ? (
|
||||||
<Label className="text-xl text-black">Penempatan File</Label>
|
<div className="mt-5">
|
||||||
{detailThumb.map((data: any) => (
|
<Label className="text-xl text-black">
|
||||||
<div key={data.id} className="flex items-center gap-3 mt-2">
|
Penempatan File
|
||||||
<img
|
</Label>
|
||||||
className="object-cover w-36 h-32"
|
{detailThumb.map((data: any) => (
|
||||||
src={data}
|
<div
|
||||||
alt={`Thumbnail ${data.id}`}
|
key={data.contentId}
|
||||||
/>
|
className="flex items-center gap-3 mt-2"
|
||||||
<div className="flex flex-row gap-3 items-center">
|
>
|
||||||
{["all", "mabes", "polda", "internasional"].map(
|
<img
|
||||||
(value) => (
|
className="object-cover w-36 h-32"
|
||||||
|
src={data}
|
||||||
|
alt={`Thumbnail ${data.contentId}`}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
{[
|
||||||
|
"all",
|
||||||
|
"mabes",
|
||||||
|
"polda",
|
||||||
|
"satker",
|
||||||
|
"internasional",
|
||||||
|
].map((value) => (
|
||||||
<label
|
<label
|
||||||
key={value}
|
key={value}
|
||||||
className="text-blue-500 cursor-pointer flex items-center gap-1"
|
className="text-blue-500 cursor-pointer flex items-center gap-1"
|
||||||
|
|
@ -651,22 +731,30 @@ export default function FormConvertSPIT() {
|
||||||
name="placement"
|
name="placement"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
handleCheckboxChangeFile(data.id, value)
|
handleCheckboxChangeFile(
|
||||||
}
|
data.contentId,
|
||||||
checked={tempFile
|
value
|
||||||
.find(
|
|
||||||
(file: any) => file.contentId === data.id
|
|
||||||
)
|
)
|
||||||
?.placement.includes(value)}
|
}
|
||||||
|
checked={
|
||||||
|
tempFile
|
||||||
|
.find(
|
||||||
|
(file: FileData) =>
|
||||||
|
file.contentId === data.contentId
|
||||||
|
)
|
||||||
|
?.placement?.includes(value) || false
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{value.charAt(0).toUpperCase() + value.slice(1)}
|
{value.charAt(0).toUpperCase() + value.slice(1)}
|
||||||
</label>
|
</label>
|
||||||
)
|
))}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,14 @@ import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import JoditEditor from "jodit-react";
|
import JoditEditor from "jodit-react";
|
||||||
import {
|
import {
|
||||||
|
acceptAssignment,
|
||||||
createAssignmentResponse,
|
createAssignmentResponse,
|
||||||
createTask,
|
createTask,
|
||||||
|
deleteAssignmentResponse,
|
||||||
|
deleteTask,
|
||||||
|
finishTask,
|
||||||
|
getAcceptance,
|
||||||
|
getAcceptanceAssignmentStatus,
|
||||||
getAssignmentResponseList,
|
getAssignmentResponseList,
|
||||||
getTask,
|
getTask,
|
||||||
getUserLevelForAssignments,
|
getUserLevelForAssignments,
|
||||||
|
|
@ -34,12 +40,14 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
import { ChevronDown, ChevronUp, DotSquare, TrashIcon } from "lucide-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { Link } from "@/components/navigation";
|
import { Link } from "@/components/navigation";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { loading } from "@/lib/swal";
|
import { close, error, loading } from "@/lib/swal";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import { successCallback } from "@/config/swal";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -64,6 +72,21 @@ export type taskDetail = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
createdBy: {
|
||||||
|
id: number;
|
||||||
|
fullname: string;
|
||||||
|
username: string | null;
|
||||||
|
email: string;
|
||||||
|
isActive: boolean;
|
||||||
|
isDefault: boolean;
|
||||||
|
isInternational: boolean;
|
||||||
|
userLevel: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
aliasName: string;
|
||||||
|
userGroupId: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
taskType: string;
|
taskType: string;
|
||||||
broadcastType: string;
|
broadcastType: string;
|
||||||
narration: string;
|
narration: string;
|
||||||
|
|
@ -85,6 +108,43 @@ const ViewEditor = dynamic(
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const formatDate = (dateString: string): string => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
|
||||||
|
// Pastikan validitas tanggal
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
throw new Error("Invalid date format");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format tanggal
|
||||||
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
|
const year = date.getFullYear();
|
||||||
|
// const hours = date.getHours().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
// Gabungkan hasil format
|
||||||
|
return `${day}-${month}-${year} `;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface AcceptanceData {
|
||||||
|
id: number;
|
||||||
|
acceptAt: string;
|
||||||
|
// Tambahkan properti lain sesuai dengan struktur data Anda
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AcceptanceData {
|
||||||
|
id: number;
|
||||||
|
userLevelId: number;
|
||||||
|
sentAt: string;
|
||||||
|
isAccept: boolean;
|
||||||
|
isSent: boolean;
|
||||||
|
userLevels: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
aliasName: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default function FormTaskDetail() {
|
export default function FormTaskDetail() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -123,6 +183,15 @@ export default function FormTaskDetail() {
|
||||||
const [showInput, setShowInput] = useState<boolean>(false);
|
const [showInput, setShowInput] = useState<boolean>(false);
|
||||||
const [listData, setListData] = useState([]);
|
const [listData, setListData] = useState([]);
|
||||||
const [message, setMessage] = useState<string>("");
|
const [message, setMessage] = useState<string>("");
|
||||||
|
const [sentAcceptance, setSentAcceptance] = useState<AcceptanceData[]>([]);
|
||||||
|
const [acceptAcceptance, setAcceptAcceptance] = useState<AcceptanceData[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [statusAcceptance, setStatusAcceptance] = useState();
|
||||||
|
const [modalType, setModalType] = useState<"terkirim" | "diterima" | "">("");
|
||||||
|
const [Isloading, setLoading] = useState<boolean>(false);
|
||||||
|
const [refreshAcceptance, setRefreshAcceptance] = useState<boolean>(false);
|
||||||
|
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
||||||
|
|
||||||
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||||
const [unitSelection, setUnitSelection] = useState({
|
const [unitSelection, setUnitSelection] = useState({
|
||||||
|
|
@ -288,6 +357,19 @@ export default function FormTaskDetail() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const successConfirm = () => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/en/contributor/task");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleCheckboxChange = (levelId: any) => {
|
const handleCheckboxChange = (levelId: any) => {
|
||||||
setCheckedLevels((prev: any) => {
|
setCheckedLevels((prev: any) => {
|
||||||
const updatedLevels = new Set(prev);
|
const updatedLevels = new Set(prev);
|
||||||
|
|
@ -307,10 +389,6 @@ export default function FormTaskDetail() {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleToggleInput = () => {
|
|
||||||
// setShowInput((prev: any) => !prev); // Toggle visibility of input field
|
|
||||||
// };
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
// loading();
|
// loading();
|
||||||
|
|
@ -324,11 +402,6 @@ export default function FormTaskDetail() {
|
||||||
initState();
|
initState();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// const handleSubmitResponse = () => {
|
|
||||||
// console.log("Response Submitted:", response);
|
|
||||||
// setShowInput(false); // Optionally hide the input after submission
|
|
||||||
// };
|
|
||||||
|
|
||||||
const handleToggleInput = (): void => {
|
const handleToggleInput = (): void => {
|
||||||
setShowInput(!showInput);
|
setShowInput(!showInput);
|
||||||
};
|
};
|
||||||
|
|
@ -356,12 +429,39 @@ export default function FormTaskDetail() {
|
||||||
sendSuggestionParent();
|
sendSuggestionParent();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sendReplyData = async (parentId: number) => {
|
||||||
|
const inputElement = document.querySelector(
|
||||||
|
`#input-comment-${parentId}`
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
|
||||||
|
if (inputElement?.value?.length > 1) {
|
||||||
|
loading();
|
||||||
|
const data = {
|
||||||
|
assignmentId: id,
|
||||||
|
message: inputElement.value,
|
||||||
|
parentId,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
const response = await createAssignmentResponse(data);
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
const responseGet = await getAssignmentResponseList(id);
|
||||||
|
console.log(responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
|
||||||
|
inputElement.value = "";
|
||||||
|
close();
|
||||||
|
setReplyingTo(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function sendSuggestionParent() {
|
async function sendSuggestionParent() {
|
||||||
if (message?.length > 1) {
|
if (message?.length > 1) {
|
||||||
loading();
|
loading();
|
||||||
const data = {
|
const data = {
|
||||||
assignmentId: id,
|
assignmentId: id,
|
||||||
message, // Gunakan response di sini
|
message,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -377,11 +477,253 @@ export default function FormTaskDetail() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteDataSuggestion(dataId: any) {
|
||||||
|
loading();
|
||||||
|
const response = await deleteAssignmentResponse(dataId);
|
||||||
|
console.log(response);
|
||||||
|
const responseGet = await getAssignmentResponseList(id);
|
||||||
|
console.log(responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteData = (dataId: any) => {
|
||||||
|
deleteDataSuggestion(dataId);
|
||||||
|
console.log(dataId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// async function sendSuggestionChild(parentId: any) {
|
||||||
|
// const msg = document.querySelectorAll(`#input-comment-${parentId}`)[0]
|
||||||
|
// .value;
|
||||||
|
|
||||||
|
// if (msg?.length > 1) {
|
||||||
|
// loading();
|
||||||
|
// const data = {
|
||||||
|
// assignmentId: id,
|
||||||
|
// message: msg,
|
||||||
|
// parentId,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// console.log(data);
|
||||||
|
// const response = await createAssignmentResponse(data);
|
||||||
|
// console.log(response);
|
||||||
|
// const responseGet = await getAssignmentResponseList(id);
|
||||||
|
// console.log(responseGet?.data?.data);
|
||||||
|
// setListData(responseGet?.data?.data);
|
||||||
|
// // $(":input").val("");
|
||||||
|
// close();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function getDataAcceptance() {
|
||||||
|
const response = await getAcceptanceAssignmentStatus(id);
|
||||||
|
setStatusAcceptance(response?.data?.data?.isAccept);
|
||||||
|
console.log("Status :", response?.data?.data?.isAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAcceptAcceptance = async () => {
|
||||||
|
const isAccept = true;
|
||||||
|
loading();
|
||||||
|
console.log("Id user :", userId);
|
||||||
|
const response = await acceptAssignment(id, !isAccept);
|
||||||
|
|
||||||
|
if (response?.error) {
|
||||||
|
error(response?.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
successCallback();
|
||||||
|
getDataAcceptance();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getListAcceptance(): Promise<void> {
|
||||||
|
const isAccept = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resSent = await getAcceptance(id, !isAccept);
|
||||||
|
setSentAcceptance(resSent?.data?.data);
|
||||||
|
|
||||||
|
const resAccept = await getAcceptance(id, isAccept);
|
||||||
|
|
||||||
|
const acceptanceSort = resAccept?.data?.data?.sort(
|
||||||
|
(a: AcceptanceData, b: AcceptanceData) =>
|
||||||
|
new Date(a.acceptAt).getTime() - new Date(b.acceptAt).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Data sort:", acceptanceSort);
|
||||||
|
setAcceptAcceptance(acceptanceSort);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching acceptance data:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initState(): Promise<void> {
|
||||||
|
await getListAcceptance();
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}, [refreshAcceptance]);
|
||||||
|
|
||||||
|
const setFinishAcceptance = async (
|
||||||
|
id: number,
|
||||||
|
isFinish: boolean
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!isFinish) {
|
||||||
|
loading();
|
||||||
|
|
||||||
|
setRefreshAcceptance((prev) => !prev);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReply = (id: any) => {
|
||||||
|
setReplyingTo(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getModalContent = (type: "terkirim" | "diterima") => (
|
||||||
|
<div className="overflow-x-auto ">
|
||||||
|
{Isloading ? (
|
||||||
|
<p>Loading...</p>
|
||||||
|
) : (
|
||||||
|
<table className="w-full border-collapse border border-gray-300">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gray-100 border-b">
|
||||||
|
<th className="px-4 py-2 text-left">Waktu</th>
|
||||||
|
<th className="px-4 py-2 text-left">Unit</th>
|
||||||
|
<th className="px-4 py-2 text-left">Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{(type === "terkirim" ? sentAcceptance : acceptAcceptance).map(
|
||||||
|
(item) => (
|
||||||
|
<tr key={item.id} className="border-b">
|
||||||
|
<td className="px-4 py-2">
|
||||||
|
{new Date(item.sentAt).toLocaleString()}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-2">{item.userLevels.name}</td>
|
||||||
|
<td className="px-4 py-2">
|
||||||
|
{type === "terkirim" ? "Terkirim" : "Diterima"}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
async function finishAssignment() {
|
||||||
|
const response = finishTask(id);
|
||||||
|
|
||||||
|
// if (response.error) {
|
||||||
|
// error(response.message);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
successConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteAssignment() {
|
||||||
|
const response = deleteTask(id);
|
||||||
|
|
||||||
|
// if (response.error) {
|
||||||
|
// error(response.message);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
successConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleForward() {
|
||||||
|
router.push(`/en/contributor/task/forward/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDeleteAssignment() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Apakah Anda yakin ingin menghapus data?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Hapus",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
deleteAssignment();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAssignmentDone() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Apakah tugas sudah selesai?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Ya",
|
||||||
|
cancelButtonText: "Tidak",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
finishAssignment();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="px-6 py-6">
|
{detail !== undefined ? (
|
||||||
<p className="text-lg font-semibold mb-3">Form Penugasan</p>
|
<div className="px-6 py-6">
|
||||||
{detail !== undefined ? (
|
<div className="flex flex-row justify-between">
|
||||||
|
<p className="text-lg font-semibold mb-3">Detail Penugasan</p>
|
||||||
|
<div
|
||||||
|
className="flex gap-3"
|
||||||
|
style={
|
||||||
|
detail?.createdBy?.id === Number(userId)
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
display: "none",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => setModalType("terkirim")}
|
||||||
|
>
|
||||||
|
{sentAcceptance?.length} Terkirim
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
color="warning"
|
||||||
|
onClick={() => setModalType("diterima")}
|
||||||
|
className="ml-3"
|
||||||
|
>
|
||||||
|
{acceptAcceptance?.length} Diterima
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Detail Status Penugasan</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
{modalType === "terkirim" && getModalContent("terkirim")}
|
||||||
|
{modalType === "diterima" && getModalContent("diterima")}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|
@ -546,7 +888,7 @@ export default function FormTaskDetail() {
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<Label>Tipe Penugasan</Label>
|
<Label>Tipe Penugasan</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup
|
value={detail.assignmentMainType.id.toString()}
|
||||||
onValueChange={(value) => setMainType(value)}
|
onValueChange={(value) => setMainType(value)}
|
||||||
// value={String(mainType)}
|
// value={String(mainType)}
|
||||||
// onValueChange={(value) => setMainType(Number(value))}
|
// onValueChange={(value) => setMainType(Number(value))}
|
||||||
|
|
@ -575,8 +917,8 @@ export default function FormTaskDetail() {
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>Jenis Penugasan</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup
|
value={detail.assignmentType.id.toString()}
|
||||||
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={(value) => setType(value)}
|
||||||
className="flex flex-wrap gap-3"
|
className="flex flex-wrap gap-3"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -661,7 +1003,17 @@ export default function FormTaskDetail() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
<Button color="primary" variant={"default"}>
|
<Button
|
||||||
|
className="btn btn-primary ml-3 mr-3"
|
||||||
|
style={
|
||||||
|
statusAcceptance || detail?.createdBy?.id == Number(userId)
|
||||||
|
? {
|
||||||
|
display: "none",
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
onClick={() => handleAcceptAcceptance()}
|
||||||
|
>
|
||||||
Terima Tugas
|
Terima Tugas
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -690,35 +1042,202 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<h3 className="text-lg font-bold">Tanggapan</h3>
|
<h3 className="text-lg font-bold text-center">Tanggapan</h3>
|
||||||
{listData?.map((item: any) => (
|
{listData?.map((item: any) => (
|
||||||
<div
|
<div key={item.id} className="flex flex-col gap-3 mt-2 ">
|
||||||
key={item.id}
|
<div className="flex flex-row gap-3">
|
||||||
className="border p-4 mt-2 rounded bg-gray-100 flex flex-col"
|
<Avatar className="mt-2">
|
||||||
>
|
<AvatarImage
|
||||||
<div className="flex justify-between items-center">
|
src={"/assets/avatar-profile.png"}
|
||||||
<span className="font-medium">{item.name}</span>
|
alt={`@${item.username}`}
|
||||||
<span className="text-sm text-gray-500">
|
/>
|
||||||
{item.timestamp}
|
</Avatar>
|
||||||
</span>
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
</div>
|
<div className="flex items-center justify-between">
|
||||||
<p className="mt-2">{item.content}</p>
|
<span className="text-gray-700 font-semibold">
|
||||||
<div className="flex gap-2 mt-2">
|
{item.suggestionFrom.username}
|
||||||
<button className="text-blue-500 hover:underline">
|
</span>
|
||||||
Balas
|
<span className="text-gray-500 text-sm">
|
||||||
</button>
|
{formatDate(item.createdAt)}
|
||||||
<button className="text-red-500 hover:underline">
|
</span>
|
||||||
Hapus
|
</div>
|
||||||
</button>
|
<p className="text-gray-800 mt-1">{item.message}</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
||||||
|
onClick={() => handleReply(item.id)}
|
||||||
|
>
|
||||||
|
<DotSquare className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Balas</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
|
onClick={() => deleteData(item.id)}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Delete</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{replyingTo === item.id && (
|
||||||
|
<div className="ml-10 mt-2">
|
||||||
|
<textarea
|
||||||
|
id={`input-comment-${item.id}`}
|
||||||
|
className="w-full p-2 border rounded"
|
||||||
|
placeholder="Masukkan tanggapan anda"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
|
||||||
|
onClick={() => sendReplyData(item.id)}
|
||||||
|
>
|
||||||
|
Kirim
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.children?.length > 0 && (
|
||||||
|
<div className="ml-10 mt-2 flex flex-col">
|
||||||
|
{item.children.map((child: any) => (
|
||||||
|
<div
|
||||||
|
key={child.id}
|
||||||
|
className="flex flex-col gap-3 mt-2"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-3">
|
||||||
|
<Avatar className="mt-2">
|
||||||
|
<AvatarImage
|
||||||
|
src={"/assets/avatar-profile.png"}
|
||||||
|
alt={`@${child.username}`}
|
||||||
|
/>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-700 font-semibold">
|
||||||
|
{child.suggestionFrom.username}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">
|
||||||
|
{formatDate(child.createdAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-800 mt-1">
|
||||||
|
{child.message}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
||||||
|
onClick={() => handleReply(child.id)}
|
||||||
|
>
|
||||||
|
<DotSquare className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Balas</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
|
onClick={() => deleteData(child.id)}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Delete</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{replyingTo === child.id && (
|
||||||
|
<div className="ml-10 mt-2">
|
||||||
|
<textarea
|
||||||
|
id={`input-comment-${child.id}`}
|
||||||
|
className="w-full p-2 border rounded"
|
||||||
|
placeholder="Masukkan tanggapan anda"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
|
||||||
|
onClick={() => sendReplyData(child.id)}
|
||||||
|
>
|
||||||
|
Kirim
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{child.children?.length > 0 && (
|
||||||
|
<div className="ml-10 mt-2 flex flex-col mb-3">
|
||||||
|
{child.children.map((child2: any) => (
|
||||||
|
<div
|
||||||
|
key={child2.id}
|
||||||
|
className="flex flex-col gap-3 mt-2"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-3 ">
|
||||||
|
<Avatar className="mt-2">
|
||||||
|
<AvatarImage
|
||||||
|
src={"/assets/avatar-profile.png"}
|
||||||
|
alt={`@${child2.username}`}
|
||||||
|
/>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-700 font-semibold">
|
||||||
|
{child2.suggestionFrom.username}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">
|
||||||
|
{formatDate(child2.createdAt)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-800 mt-1">
|
||||||
|
{child2.message}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<div
|
||||||
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
|
onClick={() => deleteData(child2.id)}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-4 h-4" />
|
||||||
|
<span className="ml-1">Delete</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center mt-4">
|
<div className="flex justify-center mt-4">
|
||||||
|
{detail?.createdBy?.id == Number(userId) &&
|
||||||
|
detail?.isDone !== true ? (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
color="warning"
|
||||||
|
variant={"outline"}
|
||||||
|
className="btn btn-outline-danger"
|
||||||
|
onClick={() => handleDeleteAssignment()}
|
||||||
|
>
|
||||||
|
Hapus Tugas
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
variant={"default"}
|
||||||
|
className="btn btn-primary ml-3"
|
||||||
|
onClick={() => handleAssignmentDone()}
|
||||||
|
>
|
||||||
|
Tugas Selesai
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
{detail?.isDone !== true &&
|
{detail?.isDone !== true &&
|
||||||
(Number(userLevelNumber) !== 3 ||
|
(Number(userLevelNumber) !== 3 ||
|
||||||
Number(userLevelNumber) == 2) ? (
|
Number(userLevelNumber) == 2) ? (
|
||||||
<Button color="primary" variant={"outline"}>
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant={"outline"}
|
||||||
|
type="button"
|
||||||
|
className="btn btn-outline-primary ml-3"
|
||||||
|
onClick={() => handleForward()}
|
||||||
|
>
|
||||||
Forward
|
Forward
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -726,10 +1245,10 @@ export default function FormTaskDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
</div>
|
||||||
""
|
) : (
|
||||||
)}
|
""
|
||||||
</div>
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -427,7 +427,7 @@ export default function FormTask() {
|
||||||
isAudioUploadFinish &&
|
isAudioUploadFinish &&
|
||||||
isTextUploadFinish
|
isTextUploadFinish
|
||||||
) {
|
) {
|
||||||
successSubmit("/in/contributor/agenda-setting");
|
successSubmit("/in/contributor/task");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,925 @@
|
||||||
|
"use client";
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import * as z from "zod";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import JoditEditor from "jodit-react";
|
||||||
|
import {
|
||||||
|
createAssignmentResponse,
|
||||||
|
createTask,
|
||||||
|
deleteAssignmentResponse,
|
||||||
|
deleteTask,
|
||||||
|
finishTask,
|
||||||
|
forwardTask,
|
||||||
|
getAcceptance,
|
||||||
|
getAssignmentResponseList,
|
||||||
|
getTask,
|
||||||
|
getUserLevelForAssignments,
|
||||||
|
} from "@/service/task";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { ChevronDown, ChevronUp, DotSquare, TrashIcon } from "lucide-react";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { close, error, loading } from "@/lib/swal";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
|
||||||
|
const taskSchema = z.object({
|
||||||
|
// uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
naration: z.string().min(2, {
|
||||||
|
message: "Narasi Penugasan harus lebih dari 2 karakter.",
|
||||||
|
}),
|
||||||
|
forwardMessage: z.string().min(2, {
|
||||||
|
message: "Narasi Penugasan harus lebih dari 2 karakter.",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type taskDetail = {
|
||||||
|
id: number;
|
||||||
|
uniqueCode: string;
|
||||||
|
title: string;
|
||||||
|
fileTypeOutput: string;
|
||||||
|
assignedToRole: string;
|
||||||
|
assignedToTopLevel: string;
|
||||||
|
assignmentType: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
assignmentMainType: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
createdBy: {
|
||||||
|
id: number;
|
||||||
|
fullname: string;
|
||||||
|
username: string | null;
|
||||||
|
email: string;
|
||||||
|
isActive: boolean;
|
||||||
|
isDefault: boolean;
|
||||||
|
isInternational: boolean;
|
||||||
|
userLevel: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
aliasName: string;
|
||||||
|
userGroupId: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
taskType: string;
|
||||||
|
broadcastType: string;
|
||||||
|
narration: string;
|
||||||
|
is_active: string;
|
||||||
|
isDone: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ListData {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ViewEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/view-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const formatDate = (dateString: string): string => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
|
||||||
|
// Pastikan validitas tanggal
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
throw new Error("Invalid date format");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format tanggal
|
||||||
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
|
const year = date.getFullYear();
|
||||||
|
// const hours = date.getHours().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
// Gabungkan hasil format
|
||||||
|
return `${day}-${month}-${year} `;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface AcceptanceData {
|
||||||
|
id: number;
|
||||||
|
acceptAt: string;
|
||||||
|
// Tambahkan properti lain sesuai dengan struktur data Anda
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AcceptanceData {
|
||||||
|
id: number;
|
||||||
|
userLevelId: number;
|
||||||
|
sentAt: string;
|
||||||
|
isAccept: boolean;
|
||||||
|
isSent: boolean;
|
||||||
|
userLevels: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
aliasName: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FormTaskForward() {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const editor = useRef(null);
|
||||||
|
type TaskSchema = z.infer<typeof taskSchema>;
|
||||||
|
const { id } = useParams() as { id: string };
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
const userLevelNumber = getCookiesDecrypt("ulne");
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
|
||||||
|
// State for various form fields
|
||||||
|
const [taskOutput, setTaskOutput] = useState({
|
||||||
|
all: false,
|
||||||
|
video: false,
|
||||||
|
audio: false,
|
||||||
|
image: false,
|
||||||
|
text: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// const [assignmentType, setAssignmentType] = useState("mediahub");
|
||||||
|
// const [assignmentCategory, setAssignmentCategory] = useState("publication");
|
||||||
|
const [mainType, setMainType] = useState<string>("1");
|
||||||
|
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
||||||
|
const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan
|
||||||
|
const [type, setType] = useState<string>("1");
|
||||||
|
const [selectedTarget, setSelectedTarget] = useState("all");
|
||||||
|
const [detail, setDetail] = useState<taskDetail>();
|
||||||
|
const [refresh] = useState(false);
|
||||||
|
const [listDest, setListDest] = useState([]); // Data Polda dan Polres
|
||||||
|
|
||||||
|
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [responses, setResponses] = useState<Response[]>([]);
|
||||||
|
const [response, setResponse] = useState<string>("");
|
||||||
|
const [showInput, setShowInput] = useState<boolean>(false);
|
||||||
|
const [listData, setListData] = useState([]);
|
||||||
|
const [message, setMessage] = useState<string>("");
|
||||||
|
const [sentAcceptance, setSentAcceptance] = useState<AcceptanceData[]>([]);
|
||||||
|
const [acceptAcceptance, setAcceptAcceptance] = useState<AcceptanceData[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [modalType, setModalType] = useState<"terkirim" | "diterima" | "">("");
|
||||||
|
const [Isloading, setLoading] = useState<boolean>(false);
|
||||||
|
const [refreshAcceptance, setRefreshAcceptance] = useState<boolean>(false);
|
||||||
|
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
||||||
|
const [forwardMessage, setForwardMessage] = useState();
|
||||||
|
const [narration, setNarration] = useState(null);
|
||||||
|
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
||||||
|
|
||||||
|
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||||
|
const [unitSelection, setUnitSelection] = useState({
|
||||||
|
allUnit: false,
|
||||||
|
mabes: false,
|
||||||
|
polda: false,
|
||||||
|
polres: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<TaskSchema>({
|
||||||
|
resolver: zodResolver(taskSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// const selectedValue = Number(event.target.value);
|
||||||
|
// setMainType(selectedValue);
|
||||||
|
|
||||||
|
// setPlatformTypeVisible(selectedValue === 2);
|
||||||
|
// };
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchPoldaPolres() {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await getUserLevelForAssignments();
|
||||||
|
setListDest(response?.data?.data.list);
|
||||||
|
const initialExpandedState = response?.data?.data.list.reduce(
|
||||||
|
(acc: any, polda: any) => {
|
||||||
|
acc[polda.id] = false;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
setExpandedPolda(initialExpandedState);
|
||||||
|
console.log("polres", initialExpandedState);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching Polda/Polres data:", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchPoldaPolres();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initState() {
|
||||||
|
if (id) {
|
||||||
|
const response = await getTask(id);
|
||||||
|
const details = response?.data?.data;
|
||||||
|
|
||||||
|
setDetail(details);
|
||||||
|
|
||||||
|
if (details?.assignedToLevel) {
|
||||||
|
const levels = new Set(
|
||||||
|
details.assignedToLevel.split(",").map(Number)
|
||||||
|
);
|
||||||
|
setCheckedLevels(levels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initState();
|
||||||
|
}, [id, refresh]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (detail?.broadcastType) {
|
||||||
|
setBroadcastType(detail.broadcastType); // Mengatur nilai broadcastType dari API
|
||||||
|
}
|
||||||
|
}, [detail?.broadcastType]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (detail?.fileTypeOutput) {
|
||||||
|
const outputSet = new Set(detail.fileTypeOutput.split(",").map(Number)); // Membagi string ke dalam array dan mengonversi ke nomor
|
||||||
|
setTaskOutput({
|
||||||
|
all: outputSet.has(0),
|
||||||
|
video: outputSet.has(2),
|
||||||
|
audio: outputSet.has(4),
|
||||||
|
image: outputSet.has(1),
|
||||||
|
text: outputSet.has(3),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [detail?.fileTypeOutput]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (detail?.assignedToTopLevel) {
|
||||||
|
const outputSet = new Set(
|
||||||
|
detail.assignedToTopLevel.split(",").map(Number)
|
||||||
|
);
|
||||||
|
setUnitSelection({
|
||||||
|
allUnit: outputSet.has(0),
|
||||||
|
mabes: outputSet.has(1),
|
||||||
|
polda: outputSet.has(2),
|
||||||
|
polres: outputSet.has(3),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [detail?.fileTypeOutput]);
|
||||||
|
|
||||||
|
const save = async (data: TaskSchema) => {
|
||||||
|
const fileTypeMapping = {
|
||||||
|
all: "1",
|
||||||
|
video: "2",
|
||||||
|
audio: "3",
|
||||||
|
image: "4",
|
||||||
|
text: "5",
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedOutputs = Object.keys(taskOutput)
|
||||||
|
.filter((key) => taskOutput[key as keyof typeof taskOutput])
|
||||||
|
.map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping])
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
const requestData = {
|
||||||
|
...data,
|
||||||
|
// assignmentType,
|
||||||
|
// assignmentCategory,
|
||||||
|
target: selectedTarget,
|
||||||
|
unitSelection,
|
||||||
|
assignedToRole: "3",
|
||||||
|
taskType: taskType,
|
||||||
|
broadcastType: broadcastType,
|
||||||
|
assignmentMainTypeId: mainType,
|
||||||
|
assignmentPurpose: "1",
|
||||||
|
assignmentTypeId: type,
|
||||||
|
fileTypeOutput: selectedOutputs,
|
||||||
|
id: null,
|
||||||
|
narration: data.naration,
|
||||||
|
platformType: "",
|
||||||
|
title: data.title,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await createTask(requestData);
|
||||||
|
|
||||||
|
console.log("Form Data Submitted:", requestData);
|
||||||
|
console.log("response", response);
|
||||||
|
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
text: "Data berhasil disimpan.",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then(() => {
|
||||||
|
router.push("/en/contributor/task");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = (data: TaskSchema) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Simpan Data",
|
||||||
|
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
save(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const successConfirm = () => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/en/contributor/task");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePoldaPolresChange = () => {
|
||||||
|
return Array.from(checkedLevels).join(",");
|
||||||
|
};
|
||||||
|
|
||||||
|
async function saveForward() {
|
||||||
|
console.log("Narasi :", narration);
|
||||||
|
loading();
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
forwardMessage,
|
||||||
|
assignedToLevel: handlePoldaPolresChange(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await forwardTask(data);
|
||||||
|
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
successConfirm();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCheckboxChange = (levelId: any) => {
|
||||||
|
setCheckedLevels((prev: any) => {
|
||||||
|
const updatedLevels = new Set(prev);
|
||||||
|
if (updatedLevels.has(levelId)) {
|
||||||
|
updatedLevels.delete(levelId);
|
||||||
|
} else {
|
||||||
|
updatedLevels.add(levelId);
|
||||||
|
}
|
||||||
|
return updatedLevels;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleExpand = (poldaId: any) => {
|
||||||
|
setExpandedPolda((prev: any) => ({
|
||||||
|
...prev,
|
||||||
|
[poldaId]: !prev[poldaId],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initState() {
|
||||||
|
// loading();
|
||||||
|
const response = await getAssignmentResponseList(id);
|
||||||
|
console.log("data", response?.data?.data);
|
||||||
|
console.log("userLvl", userLevelNumber);
|
||||||
|
setListData(response?.data?.data);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleToggleInput = (): void => {
|
||||||
|
setShowInput(!showInput);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setMessage(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const postData = () => {
|
||||||
|
sendSuggestionParent();
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendReplyData = async (parentId: number) => {
|
||||||
|
const inputElement = document.querySelector(
|
||||||
|
`#input-comment-${parentId}`
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
|
||||||
|
if (inputElement?.value?.length > 1) {
|
||||||
|
loading();
|
||||||
|
const data = {
|
||||||
|
assignmentId: id,
|
||||||
|
message: inputElement.value,
|
||||||
|
parentId,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
const response = await createAssignmentResponse(data);
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
const responseGet = await getAssignmentResponseList(id);
|
||||||
|
console.log(responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
|
||||||
|
inputElement.value = "";
|
||||||
|
close();
|
||||||
|
setReplyingTo(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function sendSuggestionParent() {
|
||||||
|
if (message?.length > 1) {
|
||||||
|
loading();
|
||||||
|
const data = {
|
||||||
|
assignmentId: id,
|
||||||
|
message,
|
||||||
|
parentId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await createAssignmentResponse(data);
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
setMessage("");
|
||||||
|
const responseGet = await getAssignmentResponseList(id);
|
||||||
|
console.log(responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteDataSuggestion(dataId: any) {
|
||||||
|
loading();
|
||||||
|
const response = await deleteAssignmentResponse(dataId);
|
||||||
|
console.log(response);
|
||||||
|
const responseGet = await getAssignmentResponseList(id);
|
||||||
|
console.log(responseGet?.data?.data);
|
||||||
|
setListData(responseGet?.data?.data);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteData = (dataId: any) => {
|
||||||
|
deleteDataSuggestion(dataId);
|
||||||
|
console.log(dataId);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getListAcceptance(): Promise<void> {
|
||||||
|
const isAccept = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resSent = await getAcceptance(id, !isAccept);
|
||||||
|
setSentAcceptance(resSent?.data?.data);
|
||||||
|
|
||||||
|
const resAccept = await getAcceptance(id, isAccept);
|
||||||
|
|
||||||
|
const acceptanceSort = resAccept?.data?.data?.sort(
|
||||||
|
(a: AcceptanceData, b: AcceptanceData) =>
|
||||||
|
new Date(a.acceptAt).getTime() - new Date(b.acceptAt).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Data sort:", acceptanceSort);
|
||||||
|
setAcceptAcceptance(acceptanceSort);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching acceptance data:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initState(): Promise<void> {
|
||||||
|
await getListAcceptance();
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}, [refreshAcceptance]);
|
||||||
|
|
||||||
|
const setFinishAcceptance = async (
|
||||||
|
id: number,
|
||||||
|
isFinish: boolean
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!isFinish) {
|
||||||
|
loading();
|
||||||
|
|
||||||
|
setRefreshAcceptance((prev) => !prev);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReply = (id: any) => {
|
||||||
|
setReplyingTo(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function finishAssignment() {
|
||||||
|
const response = finishTask(id);
|
||||||
|
|
||||||
|
// if (response.error) {
|
||||||
|
// error(response.message);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
successConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteAssignment() {
|
||||||
|
const response = deleteTask(id);
|
||||||
|
|
||||||
|
// if (response.error) {
|
||||||
|
// error(response.message);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
successConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleForward() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Forward Penugasan?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Ya",
|
||||||
|
cancelButtonText: "Tidak",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
saveForward();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function handleDeleteAssignment() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Apakah Anda yakin ingin menghapus data?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Hapus",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
deleteAssignment();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAssignmentDone() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Apakah tugas sudah selesai?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Ya",
|
||||||
|
cancelButtonText: "Tidak",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
finishAssignment();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
{detail !== undefined ? (
|
||||||
|
<div className="px-6 py-6">
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<p className="text-lg font-semibold mb-3">Tujuan Forward</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="gap-5 mb-5">
|
||||||
|
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||||
|
{listDest.map((polda: any) => (
|
||||||
|
<div key={polda.id} className="border p-2">
|
||||||
|
<Label className="flex items-center">
|
||||||
|
<Checkbox
|
||||||
|
checked={checkedLevels.has(polda.id)}
|
||||||
|
onCheckedChange={() => handleCheckboxChange(polda.id)}
|
||||||
|
className="mr-3"
|
||||||
|
/>
|
||||||
|
{polda.name}
|
||||||
|
<button
|
||||||
|
onClick={() => toggleExpand(polda.id)}
|
||||||
|
className="ml-2 focus:outline-none"
|
||||||
|
></button>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="form-group mt-2">
|
||||||
|
<Label htmlFor="message">Pesan</Label>
|
||||||
|
<Textarea
|
||||||
|
id="forwardMessage"
|
||||||
|
placeholder="Message"
|
||||||
|
{...register("forwardMessage")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant={"default"}
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary mr-2 float-right my-3"
|
||||||
|
onClick={() => handleForward()}
|
||||||
|
>
|
||||||
|
Forward Tugas
|
||||||
|
</Button>
|
||||||
|
<div className="space-y-2 mt-6">
|
||||||
|
<Label>Judul</Label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Input
|
||||||
|
size="md"
|
||||||
|
type="text"
|
||||||
|
value={detail?.title}
|
||||||
|
onChange={field.onChange}
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{errors.title?.message && (
|
||||||
|
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div className="mt-6">
|
||||||
|
<Label>Tujuan Pemilihan Tugas</Label>
|
||||||
|
<Select
|
||||||
|
onValueChange={setSelectedTarget}
|
||||||
|
value={detail.assignedToRole}
|
||||||
|
>
|
||||||
|
<SelectTrigger size="md">
|
||||||
|
<SelectValue placeholder="Pilih" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="3,4">Semua Pengguna</SelectItem>
|
||||||
|
<SelectItem value="4">Kontributor</SelectItem>
|
||||||
|
<SelectItem value="3">Approver</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-3 mt-6 pt-5 ml-3">
|
||||||
|
{Object.keys(unitSelection).map((key) => (
|
||||||
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
<Checkbox
|
||||||
|
id={key}
|
||||||
|
disabled
|
||||||
|
checked={
|
||||||
|
unitSelection[key as keyof typeof unitSelection]
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
setUnitSelection({ ...unitSelection, [key]: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={key}>
|
||||||
|
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-6 pt-5 pl-3">
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="soft" size="sm" color="primary">
|
||||||
|
[Kustom]
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
Daftar Wilayah Polda dan Polres
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||||
|
{listDest.map((polda: any) => (
|
||||||
|
<div key={polda.id} className="border p-2">
|
||||||
|
<Label className="flex items-center">
|
||||||
|
<Checkbox
|
||||||
|
disabled
|
||||||
|
checked={checkedLevels.has(polda.id)}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleCheckboxChange(polda.id)
|
||||||
|
}
|
||||||
|
className="mr-3"
|
||||||
|
/>
|
||||||
|
{polda.name}
|
||||||
|
<button
|
||||||
|
onClick={() => toggleExpand(polda.id)}
|
||||||
|
className="ml-2 focus:outline-none"
|
||||||
|
>
|
||||||
|
{expandedPolda[polda.id] ? (
|
||||||
|
<ChevronUp size={16} />
|
||||||
|
) : (
|
||||||
|
<ChevronDown size={16} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</Label>
|
||||||
|
{expandedPolda[polda.id] && (
|
||||||
|
<div className="ml-6 mt-2">
|
||||||
|
<Label className="block">
|
||||||
|
<Checkbox
|
||||||
|
disabled
|
||||||
|
checked={polda?.subDestination?.every(
|
||||||
|
(polres: any) =>
|
||||||
|
checkedLevels.has(polres.id)
|
||||||
|
)}
|
||||||
|
onCheckedChange={(isChecked) => {
|
||||||
|
const updatedLevels = new Set(
|
||||||
|
checkedLevels
|
||||||
|
);
|
||||||
|
polda?.subDestination?.forEach(
|
||||||
|
(polres: any) => {
|
||||||
|
if (isChecked) {
|
||||||
|
updatedLevels.add(polres.id);
|
||||||
|
} else {
|
||||||
|
updatedLevels.delete(polres.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setCheckedLevels(updatedLevels);
|
||||||
|
}}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
Pilih Semua Polres
|
||||||
|
</Label>
|
||||||
|
{polda?.subDestination?.map((polres: any) => (
|
||||||
|
<Label key={polres.id} className="block mt-1">
|
||||||
|
<Checkbox
|
||||||
|
disabled
|
||||||
|
checked={checkedLevels.has(polres.id)}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleCheckboxChange(polres.id)
|
||||||
|
}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
{polres.name}
|
||||||
|
</Label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<Label>Tipe Penugasan</Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={detail.assignmentMainType.id.toString()}
|
||||||
|
onValueChange={(value) => setMainType(value)}
|
||||||
|
// value={String(mainType)}
|
||||||
|
// onValueChange={(value) => setMainType(Number(value))}
|
||||||
|
className="flex flex-wrap gap-3"
|
||||||
|
>
|
||||||
|
<RadioGroupItem value="1" id="mediahub" />
|
||||||
|
<Label htmlFor="mediahub">Mediahub</Label>
|
||||||
|
<RadioGroupItem value="2" id="medsos-mediahub" />
|
||||||
|
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<Label>Jenis Tugas </Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={detail.taskType.toString()}
|
||||||
|
onValueChange={(value) => setTaskType(String(value))}
|
||||||
|
className="flex flex-wrap gap-3"
|
||||||
|
>
|
||||||
|
<RadioGroupItem value="atensi-khusus" id="khusus" />
|
||||||
|
<Label htmlFor="atensi-khusus">Atensi Khusus</Label>
|
||||||
|
<RadioGroupItem value="tugas-harian" id="harian" />
|
||||||
|
<Label htmlFor="tugas-harian">Tugas Harian</Label>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
{/* RadioGroup Assignment Category */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<Label>Jenis Penugasan</Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={detail.assignmentType.id.toString()}
|
||||||
|
onValueChange={(value) => setType(value)}
|
||||||
|
className="flex flex-wrap gap-3"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="1" id="publication" />
|
||||||
|
<Label htmlFor="publication">Publikasi</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="2" id="amplification" />
|
||||||
|
<Label htmlFor="amplification">Amplifikasi</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="3" id="contra" />
|
||||||
|
<Label htmlFor="contra">Kontra</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<Label>Output Tugas</Label>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{Object.keys(taskOutput).map((key) => (
|
||||||
|
<div className="flex items-center gap-2" key={key}>
|
||||||
|
<Checkbox
|
||||||
|
id={key}
|
||||||
|
disabled
|
||||||
|
checked={taskOutput[key as keyof typeof taskOutput]}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
setTaskOutput({ ...taskOutput, [key]: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={key}>
|
||||||
|
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* <div className="mt-6">
|
||||||
|
<Label>Broadcast </Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={broadcastType}
|
||||||
|
onValueChange={(value) => setBroadcastType(value)}
|
||||||
|
className="flex flex-wrap gap-3"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="all" id="all" />
|
||||||
|
<Label htmlFor="all">Semua</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="email" id="email" />
|
||||||
|
<Label htmlFor="email">Email Blast</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RadioGroupItem value="whatsapp" id="whatsapp" />
|
||||||
|
<Label htmlFor="whatsapp">WhatsApp Blast</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</div> */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<Label>Narasi Penugasan</Label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="naration"
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<ViewEditor initialData={detail?.narration} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{errors.naration?.message && (
|
||||||
|
<p className="text-red-400 text-sm">
|
||||||
|
{errors.naration.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,9 @@ import { useRouter } from "@/i18n/routing";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import LocalSwitcher from "../partials/header/locale-switcher";
|
import LocalSwitcher from "../partials/header/locale-switcher";
|
||||||
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||||
import { listRole } from "@/service/landing/landing";
|
import { getUserNotifications, listRole } from "@/service/landing/landing";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
|
||||||
type Detail = {
|
type Detail = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -35,6 +37,7 @@ type Detail = {
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const [inboxOpen, setInboxOpen] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const locale = params?.locale;
|
const locale = params?.locale;
|
||||||
|
|
@ -53,6 +56,10 @@ const Navbar = () => {
|
||||||
const [role, setRole] = useState<any>();
|
const [role, setRole] = useState<any>();
|
||||||
const [menuActive, setMenuActive] = useState<string>();
|
const [menuActive, setMenuActive] = useState<string>();
|
||||||
const [category, setCategory] = useState<any>();
|
const [category, setCategory] = useState<any>();
|
||||||
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
const [isMessageActive, setIsMessageActive] = useState(true);
|
||||||
|
const [notificationsUpdate, setNotificationsUpdate] = useState([]);
|
||||||
|
const [selectedTab, setSelectedTab] = useState("image");
|
||||||
|
|
||||||
let prefixPath = poldaName ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` : "/";
|
let prefixPath = poldaName ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` : "/";
|
||||||
|
|
||||||
|
|
@ -76,12 +83,44 @@ const Navbar = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initState() {
|
||||||
|
setMenuActive(menu);
|
||||||
|
const res = await listRole();
|
||||||
|
setRole(res?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotif() {
|
||||||
|
if (roleId != undefined) {
|
||||||
|
const response = await getUserNotifications(0, 2);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
console.log("respon:", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotifUpdate() {
|
||||||
|
if (roleId != undefined) {
|
||||||
|
const response = await getUserNotifications(0, 3);
|
||||||
|
setNotificationsUpdate(response?.data?.data?.content);
|
||||||
|
console.log("Notiffff:", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
getNotif();
|
||||||
|
getNotifUpdate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const test = () => {
|
||||||
|
console.log(notifications);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
const response = await getInfoProfile();
|
const response = await getInfoProfile();
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
setInboxOpen(details);
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
console.log("data", details);
|
console.log("data", details);
|
||||||
}
|
}
|
||||||
|
|
@ -93,39 +132,6 @@ const Navbar = () => {
|
||||||
console.log("Satkkeeer : ", satkerName);
|
console.log("Satkkeeer : ", satkerName);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
setMenuActive(menu);
|
|
||||||
const res = await listRole();
|
|
||||||
setRole(res?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// async function getNotif() {
|
|
||||||
// if (roleId != undefined) {
|
|
||||||
// const response = await getUserNotifications(0, 2);
|
|
||||||
// setNotifications(response?.data?.data?.content);
|
|
||||||
// console.log("respon:", response);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async function getNotifUpdate() {
|
|
||||||
// if (roleId != undefined) {
|
|
||||||
// const response = await getUserNotifications(0, 3);
|
|
||||||
// setNotificationsUpdate(response?.data?.data?.content); // console.log("respon:", response);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async function getPolritvStatus() {
|
|
||||||
// const response = await getYtPolritvStatus();
|
|
||||||
// console.log("Polritv status :", response?.data?.message);
|
|
||||||
// setIsPolriTvOnline(response.data?.message == "Online");
|
|
||||||
// }
|
|
||||||
|
|
||||||
initState();
|
|
||||||
// getNotif();
|
|
||||||
// getNotifUpdate();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChange = (e: any) => {
|
||||||
setSearch(e.target.value);
|
setSearch(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
@ -163,7 +169,7 @@ const Navbar = () => {
|
||||||
<NavigationMenu>
|
<NavigationMenu>
|
||||||
<NavigationMenuList>
|
<NavigationMenuList>
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<NavigationMenuTrigger className="">
|
<NavigationMenuTrigger>
|
||||||
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer">
|
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer">
|
||||||
<svg className="mx-2 dark:" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg className="mx-2 dark:" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
|
@ -174,42 +180,38 @@ const Navbar = () => {
|
||||||
{t("content")}
|
{t("content")}
|
||||||
</a>
|
</a>
|
||||||
</NavigationMenuTrigger>
|
</NavigationMenuTrigger>
|
||||||
<NavigationMenuContent className=" rounded-md overflow-hidden w-">
|
<NavigationMenuContent className="flex flex-col place-content-start rounded-md overflow-hidden ">
|
||||||
<NavigationMenuLink onClick={() => router.push(prefixPath+"/image/filter")} className="flex place-items-start gap-1.5 p-2">
|
<NavigationMenuLink onClick={() => router.push(prefixPath + "/image/filter")} className="flex place-items-start gap-1.5 p-2 w-36">
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row items-center py-2 cursor-pointer">
|
||||||
<FiImage className="mr-2" />
|
<FiImage className="mr-2" />
|
||||||
{t("image")}
|
{t("image")}
|
||||||
</p>
|
</p>
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
<NavigationMenuLink onClick={() => router.push(prefixPath+"/video/filter")} className="flex items-start gap-1.5 p-2 ">
|
<NavigationMenuLink onClick={() => router.push(prefixPath + "/video/filter")} className="flex items-start gap-1.5 p-2 ">
|
||||||
{pathname?.split("/")[1] == "in" ? (
|
{pathname?.split("/")[1] == "in" ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-slate-600 text-sm dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-0 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiYoutube className="mr-2" />
|
<FiYoutube className="mr-2" />
|
||||||
{t("video")}
|
{t("video")}
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiYoutube className="mr-2" />
|
<FiYoutube className="mr-2" />
|
||||||
{t("video")}
|
{t("video")}
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* <p className="text-slate-600 text-sm dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-0 py-2 cursor-pointer">
|
|
||||||
<FiYoutube className="mr-2" />
|
|
||||||
{t("video")}
|
|
||||||
</p> */}
|
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
<NavigationMenuLink onClick={() => router.push(prefixPath+"/document/filter")} className="flex place-items-start gap-1.5 p-2">
|
<NavigationMenuLink onClick={() => router.push(prefixPath + "/document/filter")} className="flex place-items-start gap-1.5 p-2">
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiFile className="mr-2" />
|
<FiFile className="mr-2" />
|
||||||
{t("text")}
|
{t("text")}
|
||||||
</p>
|
</p>
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
<NavigationMenuLink onClick={() => router.push(prefixPath+"/audio/filter")} className="flex place-items-start gap-1.5 p-2 ">
|
<NavigationMenuLink onClick={() => router.push(prefixPath + "/audio/filter")} className="flex place-items-start gap-1.5 p-2 ">
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiMusic className="mr-2" />
|
<FiMusic className="mr-2" />
|
||||||
{t("audio")}{" "}
|
{t("audio")}{" "}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -217,7 +219,7 @@ const Navbar = () => {
|
||||||
</NavigationMenuContent>
|
</NavigationMenuContent>
|
||||||
</NavigationMenuItem>
|
</NavigationMenuItem>
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<Link href={prefixPath+"/schedule"} legacyBehavior passHref>
|
<Link href={prefixPath + "/schedule"} legacyBehavior passHref>
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
<span>
|
<span>
|
||||||
<svg className="mr-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg className="mr-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
@ -232,7 +234,7 @@ const Navbar = () => {
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenuItem>
|
</NavigationMenuItem>
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<Link href={prefixPath+"/indeks"} legacyBehavior passHref>
|
<Link href={prefixPath + "/indeks"} legacyBehavior passHref>
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
<span>
|
<span>
|
||||||
<svg className="mr-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg className="mr-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
@ -251,10 +253,10 @@ const Navbar = () => {
|
||||||
</NavigationMenuList>
|
</NavigationMenuList>
|
||||||
</NavigationMenu>
|
</NavigationMenu>
|
||||||
|
|
||||||
<Link href="#" className="flex items-center space-x-1 text-red-600">
|
{/* <Link href="#" className="flex items-center space-x-1 text-red-600">
|
||||||
<span className="w-2 h-2 bg-red-500 rounded-full"></span>
|
<span className="w-2 h-2 bg-red-500 rounded-full"></span>
|
||||||
<span className="font-medium">{t("live")}</span>
|
<span className="font-medium">{t("live")}</span>
|
||||||
</Link>
|
</Link> */}
|
||||||
<div className="flex items-center space-x-1 ">
|
<div className="flex items-center space-x-1 ">
|
||||||
<a href="https://tvradio.polri.go.id/">
|
<a href="https://tvradio.polri.go.id/">
|
||||||
<img src="/assets/polriTv.png" className="object-contain h-10 flex-auto " />
|
<img src="/assets/polriTv.png" className="object-contain h-10 flex-auto " />
|
||||||
|
|
@ -336,112 +338,301 @@ const Navbar = () => {
|
||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
{roleId === "5" || roleId === "6" || roleId === "7" || roleId === "8" ? (
|
{roleId === "5" || roleId === "6" || roleId === "7" || roleId === "8" ? (
|
||||||
<DropdownMenu>
|
<>
|
||||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
{/* Inbox */}
|
||||||
{detail !== undefined ? (
|
<Popover>
|
||||||
<div className="flex items-center gap-3 text-default-800">
|
<PopoverTrigger asChild>
|
||||||
<Image src={"/assets/avatar-profile.png"} alt={"Image"} width={36} height={36} className="rounded-full" />
|
<a className="cursor-pointer" onClick={() => test()}>
|
||||||
<div>
|
{" "}
|
||||||
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">{detail?.fullname}</div>
|
<Icon icon="basil:envelope-outline" color="black" width="30" />
|
||||||
<p className="text-xs whitespace-nowrap overflow-hidden truncate">({detail?.fullname})</p>
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className=" p-0 flex flex-col mt-2" align="end">
|
||||||
|
<Tabs value={selectedTab} onValueChange={setSelectedTab} className="flex flex-col">
|
||||||
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row">
|
||||||
|
<TabsTrigger value="notif-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center justify-center cursor-pointer gap-4 rounded-lg p-3 text-sm mr-4 ${selectedTab === "notif-tab" ? "bg-[#bb3523] text-white" : "bg-gray-200 text-black"}`}
|
||||||
|
id="notif-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notif"
|
||||||
|
aria-selected="true"
|
||||||
|
onClick={() => setIsMessageActive(true)}
|
||||||
|
>
|
||||||
|
Pesan Masuk
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="notifupdate-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center cursor-pointer gap-4 rounded-lg p-3 text-sm mr-4 ${selectedTab === "notifupdate-tab" ? "bg-[#bb3523] text-white" : "bg-gray-200 text-black"}`}
|
||||||
|
id="notifupdate-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notifupdate"
|
||||||
|
aria-selected="false"
|
||||||
|
onClick={() => setIsMessageActive(false)}
|
||||||
|
>
|
||||||
|
Update(
|
||||||
|
{notificationsUpdate?.length})
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="notif-tab">
|
||||||
|
<div className="flex flex-col justify-center my-3">
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="flex flex-row" href={list.redirectUrl} key={list.id}>
|
||||||
|
<div className="ml-4">
|
||||||
|
<img src="/assets/avatar-profile.png" alt="..." className="w-8 items-center mr-3" />
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">{list?.message}</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${new Date(list?.createdAt).getMonth() + 1}/${new Date(list?.createdAt).getFullYear()} ${new Date(list?.createdAt).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox" legacyBehavior>
|
||||||
|
<p className="text-[15px] text-center py-2" role="button">
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="notifupdate-tab">
|
||||||
|
<div className="">
|
||||||
|
{notificationsUpdate?.map((list: any) => (
|
||||||
|
<a className="flex flex-row" href={list.redirectUrl} key={list.id}>
|
||||||
|
<div className="ml-4">
|
||||||
|
<img src="/assets/avatar-profile.png" alt="..." className="w-8 items-center mr-3" />
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">{list?.message}</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${new Date(list?.createdAt).getMonth() + 1}/${new Date(list?.createdAt).getFullYear()} ${new Date(list?.createdAt).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox/update" legacyBehavior>
|
||||||
|
<p className="text-[15px] text-center py-2" role="button">
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||||
|
{detail !== undefined ? (
|
||||||
|
<div className="flex items-center gap-3 text-default-800">
|
||||||
|
<Image src={"/assets/avatar-profile.png"} alt={"Image"} width={36} height={36} className="rounded-full" />
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">{detail?.fullname}</div>
|
||||||
|
<p className="text-xs whitespace-nowrap overflow-hidden truncate">({detail?.fullname})</p>
|
||||||
|
</div>
|
||||||
|
<span className="text-base me-2.5 lg:inline-block hidden">
|
||||||
|
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base me-2.5 lg:inline-block hidden">
|
) : (
|
||||||
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
""
|
||||||
</span>
|
)}
|
||||||
</div>
|
</DropdownMenuTrigger>
|
||||||
) : (
|
<DropdownMenuContent className="w-56 p-0" align="end">
|
||||||
""
|
<DropdownMenuGroup>
|
||||||
)}
|
{[
|
||||||
</DropdownMenuTrigger>
|
{
|
||||||
<DropdownMenuContent className="w-56 p-0" align="end">
|
name: "Profile & Settings",
|
||||||
<DropdownMenuGroup>
|
icon: "heroicons:user",
|
||||||
{[
|
href: "/profile",
|
||||||
{
|
},
|
||||||
name: "Profile & Settings",
|
{
|
||||||
icon: "heroicons:user",
|
name: "Kelola Konten",
|
||||||
href: "/profile",
|
icon: "stash:save-ribbon-duotone",
|
||||||
},
|
href: "/content-management/galery",
|
||||||
{
|
},
|
||||||
name: "Kelola Konten",
|
].map((item, index) => (
|
||||||
icon: "stash:save-ribbon-duotone",
|
<Link href={item.href} key={`info-menu-${index}`} className="cursor-pointer">
|
||||||
href: "/content-management/galery",
|
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
||||||
},
|
<Icon icon={item.icon} className="w-4 h-4" />
|
||||||
].map((item, index) => (
|
{item.name}
|
||||||
<Link href={item.href} key={`info-menu-${index}`} className="cursor-pointer">
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
</Link>
|
||||||
<Icon icon={item.icon} className="w-4 h-4" />
|
))}
|
||||||
{item.name}
|
</DropdownMenuGroup>
|
||||||
</DropdownMenuItem>
|
<DropdownMenuSeparator />
|
||||||
</Link>
|
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
||||||
))}
|
<div>
|
||||||
</DropdownMenuGroup>
|
<Link href={"/"}>
|
||||||
<DropdownMenuSeparator />
|
<button type="submit" className="w-full flex items-center gap-2" onClick={onLogout}>
|
||||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
<Icon icon="heroicons:power" className="w-4 h-4" />
|
||||||
<div>
|
{t("logOut")}
|
||||||
<Link href={"/"}>
|
</button>
|
||||||
<button type="submit" className="w-full flex items-center gap-2" onClick={onLogout}>
|
</Link>
|
||||||
<Icon icon="heroicons:power" className="w-4 h-4" />
|
</div>
|
||||||
{t("logOut")}
|
</DropdownMenuItem>
|
||||||
</button>
|
</DropdownMenuContent>
|
||||||
</Link>
|
</DropdownMenu>
|
||||||
</div>
|
</>
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
) : roleId === "2" || roleId === "3" || roleId === "4" || roleId === "9" || roleId === "10" || roleId === "11" || roleId === "12" || roleId === "13" ? (
|
) : roleId === "2" || roleId === "3" || roleId === "4" || roleId === "9" || roleId === "10" || roleId === "11" || roleId === "12" || roleId === "13" ? (
|
||||||
// Dropdown menu for roleId === 3
|
<>
|
||||||
<DropdownMenu>
|
{/* Inbox */}
|
||||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
<Popover>
|
||||||
{detail !== undefined ? (
|
<PopoverTrigger asChild>
|
||||||
<div className="flex items-center gap-3 text-default-800">
|
<a className="cursor-pointer" onClick={() => test()}>
|
||||||
<Image src={"/assets/avatar-profile.png"} alt={"Image"} width={36} height={36} className="rounded-full" />
|
{" "}
|
||||||
<div>
|
<Icon icon="basil:envelope-outline" color="black" width="30" />
|
||||||
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">{detail?.fullname}</div>
|
</a>
|
||||||
<p className="text-xs whitespace-nowrap overflow-hidden truncate">({detail?.fullname})</p>
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className=" p-0 h-32 flex flex-col mt-2" align="end">
|
||||||
|
<Tabs value={selectedTab} onValueChange={setSelectedTab} className="flex flex-row">
|
||||||
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||||
|
<TabsTrigger value="notif-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center justify-center cursor-pointer bg-[#bb3523] text-white gap-4 rounded-lg p-3 text-sm mr-4 ${isMessageActive ? "active" : ""}`}
|
||||||
|
id="notif-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notif"
|
||||||
|
aria-selected="true"
|
||||||
|
onClick={() => setIsMessageActive(true)}
|
||||||
|
>
|
||||||
|
Pesan Masuk
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="notifupdate-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center cursor-pointer bg-[#bb3523] text-white text-sm gap-4 rounded-lg p-3 mr-4 ${isMessageActive ? "" : "active"}`}
|
||||||
|
id="notifupdate-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notifupdate"
|
||||||
|
aria-selected="false"
|
||||||
|
onClick={() => setIsMessageActive(false)}
|
||||||
|
>
|
||||||
|
Update(
|
||||||
|
{notificationsUpdate?.length})
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
<div className={`flex justify-center my-3 ${isMessageActive ? "active" : ""}`}>
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="" href={list.redirectUrl} key={list.id}>
|
||||||
|
<div className="">
|
||||||
|
<img src="/assets/avatar-profile.png" alt="..." className="" />
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">{list?.message}</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${new Date(list?.createdAt).getMonth() + 1}/${new Date(list?.createdAt).getFullYear()} ${new Date(list?.createdAt).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox" legacyBehavior>
|
||||||
|
<p className="text-[15px] py-2" role="button">
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className={`flex flex-col justify-center my-3 ${isMessageActive ? "active" : ""}`}>
|
||||||
|
{notificationsUpdate?.map((list: any) => (
|
||||||
|
<a className="flex flex-row" href={list.redirectUrl} key={list.id}>
|
||||||
|
<div className="ml-4">
|
||||||
|
<img src="/assets/avatar-profile.png" alt="..." className="w-8 items-center mr-3" />
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">{list?.message}</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${new Date(list?.createdAt).getMonth() + 1}/${new Date(list?.createdAt).getFullYear()} ${new Date(list?.createdAt).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox" legacyBehavior>
|
||||||
|
<p className="text-[15px] text-center py-2" role="button">
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
{/* // Dropdown menu for roleId === 3 */}
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||||
|
{detail !== undefined ? (
|
||||||
|
<div className="flex items-center gap-3 text-default-800">
|
||||||
|
<Image src={"/assets/avatar-profile.png"} alt={"Image"} width={36} height={36} className="rounded-full" />
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">{detail?.fullname}</div>
|
||||||
|
<p className="text-xs whitespace-nowrap overflow-hidden truncate">({detail?.fullname})</p>
|
||||||
|
</div>
|
||||||
|
<span className="text-base me-2.5 lg:inline-block hidden">
|
||||||
|
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base me-2.5 lg:inline-block hidden">
|
) : (
|
||||||
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
""
|
||||||
</span>
|
)}
|
||||||
</div>
|
</DropdownMenuTrigger>
|
||||||
) : (
|
<DropdownMenuContent className="w-56 p-0" align="end">
|
||||||
""
|
<DropdownMenuGroup>
|
||||||
)}
|
{[
|
||||||
</DropdownMenuTrigger>
|
{
|
||||||
<DropdownMenuContent className="w-56 p-0" align="end">
|
name: "Profile & Settings",
|
||||||
<DropdownMenuGroup>
|
icon: "heroicons:user",
|
||||||
{[
|
href: "/profile",
|
||||||
{
|
},
|
||||||
name: "Profile & Settings",
|
{
|
||||||
icon: "heroicons:user",
|
name: "Dashboard",
|
||||||
href: "/profile",
|
icon: "material-symbols:dashboard",
|
||||||
},
|
href: "/dashboard",
|
||||||
{
|
},
|
||||||
name: "Dashboard",
|
].map((item, index) => (
|
||||||
icon: "material-symbols:dashboard",
|
<Link href={item.href} key={`info-menu-${index}`} className="cursor-pointer">
|
||||||
href: "/dashboard",
|
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
||||||
},
|
<Icon icon={item.icon} className="w-4 h-4" />
|
||||||
].map((item, index) => (
|
{item.name}
|
||||||
<Link href={item.href} key={`info-menu-${index}`} className="cursor-pointer">
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
</Link>
|
||||||
<Icon icon={item.icon} className="w-4 h-4" />
|
))}
|
||||||
{item.name}
|
</DropdownMenuGroup>
|
||||||
</DropdownMenuItem>
|
<DropdownMenuSeparator />
|
||||||
</Link>
|
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
||||||
))}
|
<div>
|
||||||
</DropdownMenuGroup>
|
<Link href={"/"}>
|
||||||
<DropdownMenuSeparator />
|
<button type="submit" className="w-full flex items-center gap-2" onClick={onLogout}>
|
||||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
<Icon icon="heroicons:power" className="w-4 h-4" />
|
||||||
<div>
|
{t("logOut")}
|
||||||
<Link href={"/"}>
|
</button>
|
||||||
<button type="submit" className="w-full flex items-center gap-2" onClick={onLogout}>
|
</Link>
|
||||||
<Icon icon="heroicons:power" className="w-4 h-4" />
|
</div>
|
||||||
{t("logOut")}
|
</DropdownMenuItem>
|
||||||
</button>
|
</DropdownMenuContent>
|
||||||
</Link>
|
</DropdownMenu>
|
||||||
</div>
|
</>
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
) : (
|
) : (
|
||||||
// Masuk and Daftar buttons for roleId === null
|
// Masuk and Daftar buttons for roleId === null
|
||||||
<div className="flex justify-center items-center mx-3 gap-5">
|
<div className="flex justify-center items-center mx-3 gap-5">
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ const SidebarManagement = () => {
|
||||||
// Render
|
// Render
|
||||||
if (!hasMounted) return null;
|
if (!hasMounted) return null;
|
||||||
return (
|
return (
|
||||||
<div className="p-12 w-1/3">
|
<div className="p-12 w-1/3 ">
|
||||||
<div className="border rounded-2xl border-black m-4">
|
<div className="border rounded-2xl border-black m-4">
|
||||||
<h1 className="text-xl p-5">Tentang Saya</h1>
|
<h1 className="text-xl p-5">Tentang Saya</h1>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -94,7 +94,7 @@ const SidebarManagement = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-4">
|
<div className="p-4 gap-1">
|
||||||
<Link href="/content-management/galery" className="mb-3">
|
<Link href="/content-management/galery" className="mb-3">
|
||||||
<div className={`${pathname?.includes("/content-management/galery") ? "bg-slate-500 text-white" : ""} hover:bg-slate-500 hover:text-white cursor-pointer p-4 rounded-lg flex justify-between`}>
|
<div className={`${pathname?.includes("/content-management/galery") ? "bg-slate-500 text-white" : ""} hover:bg-slate-500 hover:text-white cursor-pointer p-4 rounded-lg flex justify-between`}>
|
||||||
<div className="flex items-center gap-2 text-lg">
|
<div className="flex items-center gap-2 text-lg">
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
"@dnd-kit/core": "^6.1.0",
|
"@dnd-kit/core": "^6.1.0",
|
||||||
"@dnd-kit/modifiers": "^7.0.0",
|
"@dnd-kit/modifiers": "^7.0.0",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
|
"@dschoon/react-waves": "^4.0.3",
|
||||||
"@emoji-mart/data": "^1.2.1",
|
"@emoji-mart/data": "^1.2.1",
|
||||||
"@emoji-mart/react": "^1.1.1",
|
"@emoji-mart/react": "^1.1.1",
|
||||||
"@fullcalendar/core": "^6.1.15",
|
"@fullcalendar/core": "^6.1.15",
|
||||||
|
|
@ -64,6 +65,7 @@
|
||||||
"@types/react-html-parser": "^2.0.6",
|
"@types/react-html-parser": "^2.0.6",
|
||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"@vercel/analytics": "^1.3.1",
|
"@vercel/analytics": "^1.3.1",
|
||||||
|
"@wavesurfer/react": "^1.0.8",
|
||||||
"apexcharts": "^3.49.2",
|
"apexcharts": "^3.49.2",
|
||||||
"axios": "^1.7.8",
|
"axios": "^1.7.8",
|
||||||
"chart.js": "^4.4.3",
|
"chart.js": "^4.4.3",
|
||||||
|
|
@ -137,6 +139,7 @@
|
||||||
"tus-js-client": "^4.2.3",
|
"tus-js-client": "^4.2.3",
|
||||||
"use-places-autocomplete": "^4.0.1",
|
"use-places-autocomplete": "^4.0.1",
|
||||||
"vaul": "^0.9.1",
|
"vaul": "^0.9.1",
|
||||||
|
"wavesurfer.js": "^7.8.15",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
|
@ -792,6 +795,60 @@
|
||||||
"react": ">=16.8.0"
|
"react": ">=16.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@dschoon/react-waves": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dschoon/react-waves/-/react-waves-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-QlygFXBarpdRB/JhFaHkpqOVHfgmODShVkvB96g4JBIidDhhuWidfrudQSo9PxhQnLjAhSlxJjg+8ct7K8TCWw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/jest": "^26.0.15",
|
||||||
|
"@types/node": "^14.14.7",
|
||||||
|
"@types/react": "^16.9.56",
|
||||||
|
"@types/react-dom": "^16.9.9",
|
||||||
|
"typescript": "^4.0.5",
|
||||||
|
"wavesurfer.js": "^4.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8",
|
||||||
|
"yarn": "^1.9.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react": "^15.0.0 || ^16.0.0 || ^17.0.0",
|
||||||
|
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dschoon/react-waves/node_modules/@types/node": {
|
||||||
|
"version": "14.18.63",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz",
|
||||||
|
"integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@dschoon/react-waves/node_modules/@types/react": {
|
||||||
|
"version": "16.14.62",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.62.tgz",
|
||||||
|
"integrity": "sha512-BWf7hqninZav6nerxXj+NeZT/mTpDeG6Lk2zREHAy63CrnXoOGPGtNqTFYFN/sqpSaREDP5otVV88axIXmKfGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/prop-types": "*",
|
||||||
|
"@types/scheduler": "^0.16",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dschoon/react-waves/node_modules/typescript": {
|
||||||
|
"version": "4.9.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dschoon/react-waves/node_modules/wavesurfer.js": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-+nn6VD86pTtRu9leVNXoIGOCMJyaTNsKNy9v+SfUsYo+SxLCQvEzrZZ/eKMImqspsk+BX1V1xlY4FRkHswu3fA=="
|
||||||
|
},
|
||||||
"node_modules/@emoji-mart/data": {
|
"node_modules/@emoji-mart/data": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
||||||
|
|
@ -1328,6 +1385,21 @@
|
||||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jest/types": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||||
|
"@types/istanbul-reports": "^3.0.0",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/yargs": "^15.0.0",
|
||||||
|
"chalk": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||||
|
|
@ -3582,6 +3654,36 @@
|
||||||
"domelementtype": "1"
|
"domelementtype": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/istanbul-lib-coverage": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/istanbul-lib-report": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-coverage": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/istanbul-reports": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-report": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/jest": {
|
||||||
|
"version": "26.0.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz",
|
||||||
|
"integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==",
|
||||||
|
"dependencies": {
|
||||||
|
"jest-diff": "^26.0.0",
|
||||||
|
"pretty-format": "^26.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/jquery": {
|
"node_modules/@types/jquery": {
|
||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz",
|
||||||
|
|
@ -3692,6 +3794,14 @@
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-dom": {
|
||||||
|
"version": "16.9.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.25.tgz",
|
||||||
|
"integrity": "sha512-ZK//eAPhwft9Ul2/Zj+6O11YR6L4JX0J2sVeBC9Ft7x7HFN7xk7yUV/zDxqV6rjvqgl6r8Dq7oQImxtyf/Mzcw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react-geocode": {
|
"node_modules/@types/react-geocode": {
|
||||||
"version": "0.2.4",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-geocode/-/react-geocode-0.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-geocode/-/react-geocode-0.2.4.tgz",
|
||||||
|
|
@ -3737,6 +3847,11 @@
|
||||||
"integrity": "sha512-qpstuHivwg/HoXxRrBo5/r/OVx5M2SkqJpVu2haasdLctt+jMGHWjqdbI0LL7Rk2wRmN/UHdHK4JZg9RUMcvKA==",
|
"integrity": "sha512-qpstuHivwg/HoXxRrBo5/r/OVx5M2SkqJpVu2haasdLctt+jMGHWjqdbI0LL7Rk2wRmN/UHdHK4JZg9RUMcvKA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/scheduler": {
|
||||||
|
"version": "0.16.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||||
|
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
|
||||||
|
},
|
||||||
"node_modules/@types/sizzle": {
|
"node_modules/@types/sizzle": {
|
||||||
"version": "2.3.9",
|
"version": "2.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz",
|
||||||
|
|
@ -3748,6 +3863,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
|
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/yargs": {
|
||||||
|
"version": "15.0.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz",
|
||||||
|
"integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/yargs-parser": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/yargs-parser": {
|
||||||
|
"version": "21.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
|
||||||
|
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
|
||||||
|
|
@ -3917,6 +4045,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@wavesurfer/react": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wavesurfer/react/-/react-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-YowJcynQTTC1oWBWlxr7IkZn9QlYfvOC2rYqOvVA2DwBh+nMEoZca7718disoFNnnX7/NzTKqZd/VuWI5RbhMQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"wavesurfer.js": ">=7.8.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||||
|
|
@ -4236,7 +4373,6 @@
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -4250,7 +4386,6 @@
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -4893,7 +5028,6 @@
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.1.0",
|
"ansi-styles": "^4.1.0",
|
||||||
"supports-color": "^7.1.0"
|
"supports-color": "^7.1.0"
|
||||||
|
|
@ -6315,6 +6449,14 @@
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/diff-sequences": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
|
|
@ -8065,7 +8207,6 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -9442,6 +9583,28 @@
|
||||||
"integrity": "sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ==",
|
"integrity": "sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-diff": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"diff-sequences": "^26.6.2",
|
||||||
|
"jest-get-type": "^26.3.0",
|
||||||
|
"pretty-format": "^26.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-get-type": {
|
||||||
|
"version": "26.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
|
||||||
|
"integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-worker": {
|
"node_modules/jest-worker": {
|
||||||
"version": "26.6.2",
|
"version": "26.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
|
||||||
|
|
@ -13357,6 +13520,25 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pretty-format": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"ansi-regex": "^5.0.0",
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"react-is": "^17.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pretty-format/node_modules/react-is": {
|
||||||
|
"version": "17.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||||
|
},
|
||||||
"node_modules/prismjs": {
|
"node_modules/prismjs": {
|
||||||
"version": "1.29.0",
|
"version": "1.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||||
|
|
@ -15631,7 +15813,6 @@
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-flag": "^4.0.0"
|
"has-flag": "^4.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -16977,6 +17158,11 @@
|
||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wavesurfer.js": {
|
||||||
|
"version": "7.8.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.8.15.tgz",
|
||||||
|
"integrity": "sha512-fWNnQt5BEGzuoJ7HRxfvpT1rOEI1AmCGPZ/+7QDkDVN/m2vIBeLVQ+5vENRMz1YwvZ/u1No0UV492/o8G++KXQ=="
|
||||||
|
},
|
||||||
"node_modules/web-namespaces": {
|
"node_modules/web-namespaces": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
"@dnd-kit/core": "^6.1.0",
|
"@dnd-kit/core": "^6.1.0",
|
||||||
"@dnd-kit/modifiers": "^7.0.0",
|
"@dnd-kit/modifiers": "^7.0.0",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
|
"@dschoon/react-waves": "^4.0.3",
|
||||||
"@emoji-mart/data": "^1.2.1",
|
"@emoji-mart/data": "^1.2.1",
|
||||||
"@emoji-mart/react": "^1.1.1",
|
"@emoji-mart/react": "^1.1.1",
|
||||||
"@fullcalendar/core": "^6.1.15",
|
"@fullcalendar/core": "^6.1.15",
|
||||||
|
|
@ -65,6 +66,7 @@
|
||||||
"@types/react-html-parser": "^2.0.6",
|
"@types/react-html-parser": "^2.0.6",
|
||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"@vercel/analytics": "^1.3.1",
|
"@vercel/analytics": "^1.3.1",
|
||||||
|
"@wavesurfer/react": "^1.0.8",
|
||||||
"apexcharts": "^3.49.2",
|
"apexcharts": "^3.49.2",
|
||||||
"axios": "^1.7.8",
|
"axios": "^1.7.8",
|
||||||
"chart.js": "^4.4.3",
|
"chart.js": "^4.4.3",
|
||||||
|
|
@ -138,6 +140,7 @@
|
||||||
"tus-js-client": "^4.2.3",
|
"tus-js-client": "^4.2.3",
|
||||||
"use-places-autocomplete": "^4.0.1",
|
"use-places-autocomplete": "^4.0.1",
|
||||||
"vaul": "^0.9.1",
|
"vaul": "^0.9.1",
|
||||||
|
"wavesurfer.js": "^7.8.15",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
583
pnpm-lock.yaml
583
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -8,18 +8,10 @@ import {
|
||||||
} from "./http-config/http-interceptor-service";
|
} from "./http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function login(data: any) {
|
export async function login(data: any) {
|
||||||
|
|
||||||
const res = await getCsrfToken();
|
|
||||||
const csrfToken = res?.data?.token;
|
|
||||||
|
|
||||||
|
|
||||||
console.log("Token CSRF : ", csrfToken);
|
|
||||||
|
|
||||||
const pathUrl = "signin";
|
const pathUrl = "signin";
|
||||||
const headers = {
|
const headers = {
|
||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
'X-XSRF-TOKEN': csrfToken
|
|
||||||
};
|
};
|
||||||
return httpPost(pathUrl, headers, data);
|
return httpPost(pathUrl, headers, data);
|
||||||
}
|
}
|
||||||
|
|
@ -89,4 +81,51 @@ export async function saveUser(data: any) {
|
||||||
const url = "users/save";
|
const url = "users/save";
|
||||||
return httpPostInterceptor(url, data);
|
return httpPostInterceptor(url, data);
|
||||||
}
|
}
|
||||||
|
export async function saveInstitutes(data: any) {
|
||||||
|
const url = "public/users/save-institutes";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
export async function postRegistration(data: any) {
|
||||||
|
const url = "public/users/save";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataByNIK(reqid: any, nik: any) {
|
||||||
|
const url = `http://spitpolri.com/api/back_end/get_ktp?reqid=${reqid}&nik=${nik}`;
|
||||||
|
return getAPIDummy(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listInstitusi(roleId: any) {
|
||||||
|
const url = `public/users/institutes?categoryRoleId=${roleId}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listProvince() {
|
||||||
|
const url = "public/users/provinces";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listCity(id: any) {
|
||||||
|
const url = `public/users/cities?provId=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listDistricts(id: any) {
|
||||||
|
const url = `public/users/districts?cityId=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataByNRP(reqid: any, nrp: any) {
|
||||||
|
const url = `http://spitpolri.com/api/back_end/get_nrp?reqid=${reqid}&nrp=${nrp}`;
|
||||||
|
return getAPIDummy(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataJournalist(cert: any) {
|
||||||
|
const url = `public/users/search-journalist?cert=${cert}`;
|
||||||
|
return httpGetInterceptor({ url });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataPersonil(nrp: any) {
|
||||||
|
const url = `public/users/search-personil?nrp=${nrp}`;
|
||||||
|
return httpGetInterceptor({ url });
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,37 @@
|
||||||
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
|
httpGetInterceptor,
|
||||||
|
httpPostInterceptor,
|
||||||
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function detailMedia(id: any) {
|
export async function detailMedia(id: any) {
|
||||||
const url = `media?id=${id}`;
|
const url = `media?id=${id}`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function listCuratedContent(
|
||||||
|
title: string = "",
|
||||||
|
limit: any,
|
||||||
|
page: any,
|
||||||
|
typeId: any,
|
||||||
|
curationType: any
|
||||||
|
// categoryId = ""
|
||||||
|
) {
|
||||||
|
const url = `media/curation/list?title=${title}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=${typeId}&curationType=${curationType}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMediaCurationMessage(id: any) {
|
||||||
|
const url = `media/curation/message/list?mediaFileId=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveMediaCurationMessage(data: any) {
|
||||||
|
const url = "media/curation/message";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMediaCurationMessage(id: any) {
|
||||||
|
const url = `media/curation/message?id=${id}`;
|
||||||
|
return httpDeleteInterceptor(url);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,24 @@ import axiosBaseInstance from "./axios-base-instance";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
|
import { getCsrfToken } from "../auth";
|
||||||
|
|
||||||
export async function httpPost(pathUrl: any, headers: any, data?: any) {
|
export async function httpPost(pathUrl: any, headers: any, data?: any) {
|
||||||
const response = await axiosBaseInstance
|
const resCsrf = await getCsrfToken();
|
||||||
.post(pathUrl, data, { headers })
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
|
||||||
|
const defaultHeaders = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergedHeaders = {
|
||||||
|
...defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axiosBaseInstance
|
||||||
|
.post(pathUrl, data, { headers: mergedHeaders })
|
||||||
.catch(function (error: any) {
|
.catch(function (error: any) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return error.response;
|
return error.response;
|
||||||
|
|
@ -50,8 +64,21 @@ export async function httpGet(pathUrl: any, headers: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function httpPut(pathUrl: any, headers: any, data?: any) {
|
export async function httpPut(pathUrl: any, headers: any, data?: any) {
|
||||||
|
const resCsrf = await getCsrfToken();
|
||||||
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
|
||||||
|
const defaultHeaders = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergedHeaders = {
|
||||||
|
...defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
const response = await axiosBaseInstance
|
const response = await axiosBaseInstance
|
||||||
.put(pathUrl, data, { headers })
|
.put(pathUrl, data, { headers: mergedHeaders })
|
||||||
.catch(function (error: any) {
|
.catch(function (error: any) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return error.response;
|
return error.response;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { httpPost } from "../http-config/http-base-service";
|
||||||
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function getHeroData() {
|
export async function getHeroData() {
|
||||||
|
|
@ -113,3 +114,17 @@ export async function getPublicSuggestionList(id: any) {
|
||||||
const url = `media/public/suggestion?mediaId=${id}`;
|
const url = `media/public/suggestion?mediaId=${id}`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
export async function verifyOTP(email: any, otp: any) {
|
||||||
|
const url = `public/users/verify-otp?email=${email}&otp=${otp}`;
|
||||||
|
return httpPostInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function requestOTP(data: any) {
|
||||||
|
const url = "public/users/otp-request";
|
||||||
|
return httpPost(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserNotifications(page = 0, typeId: any) {
|
||||||
|
const url = `notification/public/list?enablePage=1&page=${page}&typeId=${typeId}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
|
import { httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function getMediaTrackingMonitoring(page: number, size: number) {
|
export async function getMediaTrackingMonitoring(page: number, size: number) {
|
||||||
const url = `cekmedsos/monitoring/pagination?page=${page}&size=${size}`;
|
const url = `cekmedsos/monitoring/pagination?page=${page}&size=${size}`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function sendMediaUploadToEmail(data: any) {
|
||||||
|
const url = "media/public/share-to-email";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,6 @@ export async function forwardTask(data: any) {
|
||||||
return httpPostInterceptor(url, data);
|
return httpPostInterceptor(url, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function finishTask(id: any) {
|
|
||||||
const url = `assignment/finish?id=${id}`;
|
|
||||||
return httpPostInterceptor(url, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteTask(id: any) {
|
export async function deleteTask(id: any) {
|
||||||
const url = `assignment?id=${id}`;
|
const url = `assignment?id=${id}`;
|
||||||
return httpDeleteInterceptor(url);
|
return httpDeleteInterceptor(url);
|
||||||
|
|
@ -58,7 +53,7 @@ export async function getUserLevelForAssignments() {
|
||||||
|
|
||||||
export async function getAssignmentResponseList(id: any) {
|
export async function getAssignmentResponseList(id: any) {
|
||||||
const url = `assignment/response?assignmentId=${id}`;
|
const url = `assignment/response?assignmentId=${id}`;
|
||||||
return httpGetInterceptor({ url });
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAssignmentResponse(data: any) {
|
export async function createAssignmentResponse(data: any) {
|
||||||
|
|
@ -73,7 +68,7 @@ export async function deleteAssignmentResponse(id: any) {
|
||||||
|
|
||||||
export async function getAcceptance(id: any, isAccept: any) {
|
export async function getAcceptance(id: any, isAccept: any) {
|
||||||
const url = `assignment/acceptance?id=${id}&isAccept=${isAccept}`;
|
const url = `assignment/acceptance?id=${id}&isAccept=${isAccept}`;
|
||||||
return httpGetInterceptor({ url });
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function acceptAssignment(id: any, isAccept: any) {
|
export async function acceptAssignment(id: any, isAccept: any) {
|
||||||
|
|
@ -95,3 +90,8 @@ export async function getListAttachment(assignmentId: any, isForCreator: any) {
|
||||||
const url = `media/list?&enablePage=0&assignmentId=${assignmentId}&isForAdmin=${isForCreator}`;
|
const url = `media/list?&enablePage=0&assignmentId=${assignmentId}&isForAdmin=${isForCreator}`;
|
||||||
return httpGetInterceptor({ url });
|
return httpGetInterceptor({ url });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function finishTask(id: any) {
|
||||||
|
const url = `assignment/finish?id=${id}`;
|
||||||
|
return httpPostInterceptor(url);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,3 +100,9 @@ export function getLocaleTime(d: Date) {
|
||||||
const pad = (n: number, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
const pad = (n: number, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
}
|
}
|
||||||
|
export function getTimestamp(d: Date) {
|
||||||
|
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
|
return `${pad(d.getFullYear(), 4)}-${pad(d.getMonth() + 1)}-${pad(
|
||||||
|
d.getDate()
|
||||||
|
)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1 @@
|
||||||
#!/bin/sh
|
../typescript/bin/tsc
|
||||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
||||||
|
|
||||||
case `uname` in
|
|
||||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -x "$basedir/node" ]; then
|
|
||||||
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
|
||||||
else
|
|
||||||
exec node "$basedir/../typescript/bin/tsc" "$@"
|
|
||||||
fi
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
@ECHO off
|
|
||||||
GOTO start
|
|
||||||
:find_dp0
|
|
||||||
SET dp0=%~dp0
|
|
||||||
EXIT /b
|
|
||||||
:start
|
|
||||||
SETLOCAL
|
|
||||||
CALL :find_dp0
|
|
||||||
|
|
||||||
IF EXIST "%dp0%\node.exe" (
|
|
||||||
SET "_prog=%dp0%\node.exe"
|
|
||||||
) ELSE (
|
|
||||||
SET "_prog=node"
|
|
||||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
||||||
)
|
|
||||||
|
|
||||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/env pwsh
|
|
||||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
||||||
|
|
||||||
$exe=""
|
|
||||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
|
||||||
# Fix case when both the Windows and Linux builds of Node
|
|
||||||
# are installed in the same directory
|
|
||||||
$exe=".exe"
|
|
||||||
}
|
|
||||||
$ret=0
|
|
||||||
if (Test-Path "$basedir/node$exe") {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
} else {
|
|
||||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
} else {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
} else {
|
|
||||||
& "node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
}
|
|
||||||
exit $ret
|
|
||||||
|
|
@ -1,12 +1 @@
|
||||||
#!/bin/sh
|
../typescript/bin/tsserver
|
||||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
||||||
|
|
||||||
case `uname` in
|
|
||||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -x "$basedir/node" ]; then
|
|
||||||
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
|
||||||
else
|
|
||||||
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
|
||||||
fi
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
@ECHO off
|
|
||||||
GOTO start
|
|
||||||
:find_dp0
|
|
||||||
SET dp0=%~dp0
|
|
||||||
EXIT /b
|
|
||||||
:start
|
|
||||||
SETLOCAL
|
|
||||||
CALL :find_dp0
|
|
||||||
|
|
||||||
IF EXIST "%dp0%\node.exe" (
|
|
||||||
SET "_prog=%dp0%\node.exe"
|
|
||||||
) ELSE (
|
|
||||||
SET "_prog=node"
|
|
||||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
||||||
)
|
|
||||||
|
|
||||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsserver" %*
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/env pwsh
|
|
||||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
||||||
|
|
||||||
$exe=""
|
|
||||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
|
||||||
# Fix case when both the Windows and Linux builds of Node
|
|
||||||
# are installed in the same directory
|
|
||||||
$exe=".exe"
|
|
||||||
}
|
|
||||||
$ret=0
|
|
||||||
if (Test-Path "$basedir/node$exe") {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
} else {
|
|
||||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
} else {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
} else {
|
|
||||||
& "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
}
|
|
||||||
exit $ret
|
|
||||||
Loading…
Reference in New Issue