feat:update curated content,kontent
This commit is contained in:
parent
079d238d1f
commit
35e96a7bee
|
|
@ -21,8 +21,20 @@ 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,
|
||||||
|
Music,
|
||||||
|
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";
|
||||||
|
|
@ -33,6 +45,15 @@ import "swiper/css";
|
||||||
import "swiper/css/navigation";
|
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 WavesurferPlayer from "@wavesurfer/react";
|
||||||
|
import WaveSurfer from "wavesurfer.js";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { htmlToString } from "@/utils/globals";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { loading } from "@/lib/swal";
|
||||||
|
import { formatDate } from "@fullcalendar/core/index.js";
|
||||||
|
|
||||||
const detailSchema = z.object({
|
const detailSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -69,6 +90,13 @@ export type curationDetail = {
|
||||||
userGroupId: number;
|
userGroupId: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
publishedFor: string; // ID for selected radio button
|
||||||
|
publishedForObject: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
isInternal: boolean;
|
||||||
|
code: string;
|
||||||
|
}[];
|
||||||
tags: string;
|
tags: string;
|
||||||
provinceId: string;
|
provinceId: string;
|
||||||
is_active: string;
|
is_active: string;
|
||||||
|
|
@ -119,10 +147,14 @@ export default function DetailAudio() {
|
||||||
// files: [],
|
// files: [],
|
||||||
// fileType: null,
|
// fileType: null,
|
||||||
// });
|
// });
|
||||||
|
const userLevelNumber = getCookiesDecrypt("ulne");
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
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 [detailAudio, setDetailAudio] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
const [selectedValue, setSelectedValue] = useState<string>("");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|
@ -136,11 +168,30 @@ export default function DetailAudio() {
|
||||||
const [commentsData, setCommentsData] = useState(initialComments);
|
const [commentsData, setCommentsData] = useState(initialComments);
|
||||||
const [replyText, setReplyText] = useState("");
|
const [replyText, setReplyText] = useState("");
|
||||||
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
||||||
|
const [selectedFileId, setSelectedFileId] = useState(null);
|
||||||
|
const [listData, setListData] = useState([]);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
|
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
|
|
||||||
const handleReply = (commentId: number) => {
|
const handleReply = (commentId: number) => {
|
||||||
setReplyingTo(commentId);
|
setReplyingTo(commentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setMessage(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReady = (ws: any) => {
|
||||||
|
setWavesurfer(ws);
|
||||||
|
setIsPlaying(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPlayPause = () => {
|
||||||
|
wavesurfer && wavesurfer.playPause();
|
||||||
|
};
|
||||||
|
|
||||||
const addReply = (commentId: number) => {
|
const addReply = (commentId: number) => {
|
||||||
if (replyText.trim()) {
|
if (replyText.trim()) {
|
||||||
const newCommentData = commentsData.map((comment: any) => {
|
const newCommentData = commentsData.map((comment: any) => {
|
||||||
|
|
@ -166,23 +217,131 @@ export default function DetailAudio() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
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 = 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
const response = await detailMedia(id);
|
const response = await detailMedia(id);
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
|
||||||
setDetail(details);
|
|
||||||
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,
|
||||||
|
secondaryUrl: file.secondaryUrl || "default-image.jpg",
|
||||||
|
placements: file.placements || "",
|
||||||
|
}));
|
||||||
|
setDetail(details);
|
||||||
|
setSelectedValue(details?.publishedFor || "");
|
||||||
|
setSelectedFileId(details?.files[0]?.id);
|
||||||
|
setDetailAudio(fileUrls);
|
||||||
|
const fileUrlsThumbnail = filesData.map(
|
||||||
|
(file: { thumbnailFileUrl: string; placements: string }) => ({
|
||||||
|
thumbnailFileUrl: file.thumbnailFileUrl
|
||||||
|
? file.thumbnailFileUrl
|
||||||
|
: "default-image.jpg",
|
||||||
|
placements: file.placements || "",
|
||||||
|
})
|
||||||
);
|
);
|
||||||
setDetailThumb(fileUrls);
|
|
||||||
|
setDetailThumb(fileUrlsThumbnail);
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValueChange = (value: string) => {
|
||||||
|
setSelectedValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-10">
|
<div className="flex gap-10">
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -242,7 +401,7 @@ export default function DetailAudio() {
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Textarea
|
<Textarea
|
||||||
value={detail.description}
|
value={htmlToString(detail.description)}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
placeholder="Enter Meta"
|
placeholder="Enter Meta"
|
||||||
cols={5}
|
cols={5}
|
||||||
|
|
@ -263,24 +422,25 @@ export default function DetailAudio() {
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>Jenis Penugasan</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
// value={type} // State yang dipetakan ke value RadioGroup
|
value={selectedValue} // Set selected value
|
||||||
// onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={handleValueChange} // Update state on change
|
||||||
className="flex flex-wrap gap-3"
|
className="flex flex-wrap gap-3"
|
||||||
>
|
>
|
||||||
|
{/* Static list of radio buttons */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="1" id="publication" />
|
<RadioGroupItem value="5" id="umum" />
|
||||||
<Label htmlFor="umum">Umum</Label>
|
<Label htmlFor="umum">Umum</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="2" id="amplification" />
|
<RadioGroupItem value="8" id="ksp" />
|
||||||
<Label htmlFor="ksp">Ksp</Label>
|
<Label htmlFor="ksp">Ksp</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="3" id="contra" />
|
<RadioGroupItem value="6" id="journalist" />
|
||||||
<Label htmlFor="journalist">Journalist</Label>
|
<Label htmlFor="journalist">Journalist</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="4" id="contra" />
|
<RadioGroupItem value="7" id="polri" />
|
||||||
<Label htmlFor="polri">Polri</Label>
|
<Label htmlFor="polri">Polri</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
@ -288,9 +448,16 @@ export default function DetailAudio() {
|
||||||
<div className=" py-3">
|
<div className=" py-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Tag</Label>
|
<Label>Tag</Label>
|
||||||
<p className="border rounded-md text-blue-600 px-2 py-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{detail?.tags}
|
{detail?.tags?.split(",").map((tag, index) => (
|
||||||
</p>
|
<Badge
|
||||||
|
key={index}
|
||||||
|
className="border rounded-md px-2 py-2"
|
||||||
|
>
|
||||||
|
{tag.trim()}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className=" py-3">
|
<div className=" py-3">
|
||||||
|
|
@ -327,40 +494,62 @@ export default function DetailAudio() {
|
||||||
navigation={false}
|
navigation={false}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{detailThumb?.map((data: any) => (
|
{detailAudio?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide
|
||||||
<audio
|
key={data.id}
|
||||||
className="w-full"
|
onClick={() => handleFileClick(data.id)}
|
||||||
src={data}
|
>
|
||||||
controls
|
<WavesurferPlayer
|
||||||
title={`Audio ${data.id}`}
|
height={500}
|
||||||
|
waveColor="red"
|
||||||
|
url={data.secondaryUrl}
|
||||||
|
onReady={onReady}
|
||||||
|
key={`File ID: ${data.id}`}
|
||||||
|
onPlay={() => setIsPlaying(true)}
|
||||||
|
onPause={() => setIsPlaying(false)}
|
||||||
/>
|
/>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
<div className="mt-2">
|
<Button
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={onPlayPause}
|
||||||
|
disabled={isPlaying}
|
||||||
|
className={`flex items-center gap-2 ${
|
||||||
|
isPlaying
|
||||||
|
? "bg-gray-300 cursor-not-allowed"
|
||||||
|
: "bg-primary text-white"
|
||||||
|
} p-2 rounded`}
|
||||||
|
>
|
||||||
|
{isPlaying ? "Pause" : "Play"}
|
||||||
|
<Icon
|
||||||
|
icon={
|
||||||
|
isPlaying
|
||||||
|
? "carbon:pause-outline"
|
||||||
|
: "famicons:play-sharp"
|
||||||
|
}
|
||||||
|
className="h-5 w-5"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<div className=" mt-2 ">
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
onSwiper={setThumbsSwiper}
|
||||||
slidesPerView={6}
|
slidesPerView={6}
|
||||||
spaceBetween={8}
|
spaceBetween={8}
|
||||||
pagination={{
|
pagination={{ clickable: true }}
|
||||||
clickable: true,
|
|
||||||
}}
|
|
||||||
modules={[Pagination, Thumbs]}
|
modules={[Pagination, Thumbs]}
|
||||||
>
|
>
|
||||||
{detailThumb?.map((data: any) => (
|
{detailAudio?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide
|
||||||
<div className="flex items-center space-x-2">
|
key={data.id}
|
||||||
<span className="text-sm text-gray-700">
|
onClick={() => handleFileClick(data.id)}
|
||||||
Audio {data.id}
|
>
|
||||||
</span>
|
<img
|
||||||
<audio
|
className="object-fill h-full w-full rounded-md"
|
||||||
className="h-[24px] w-[80px]"
|
src={"/assets/music-icon.jpg"}
|
||||||
src={data}
|
alt={`File ID: ${data.id}`}
|
||||||
controls
|
/>
|
||||||
title={`Audio ${data.id}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
|
@ -390,35 +579,33 @@ export default function DetailAudio() {
|
||||||
src={data.thumbnailUrl} // Assuming `thumbnailUrl` is the property that contains the URL for the thumbnail image
|
src={data.thumbnailUrl} // Assuming `thumbnailUrl` is the property that contains the URL for the thumbnail image
|
||||||
alt={`Thumbnail ${index}`}
|
alt={`Thumbnail ${index}`}
|
||||||
/> */}
|
/> */}
|
||||||
<img
|
<Music className="object-cover w-32 h-32" />
|
||||||
className="object-cover w-36 h-32"
|
|
||||||
src={data}
|
|
||||||
alt={`Article ${data.id}`}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row gap-3 items-center">
|
<div className="flex flex-row gap-3 items-center">
|
||||||
<label className="text-blue-500 cursor-pointer">
|
{/* Mabes Checkbox */}
|
||||||
<input
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
type="checkbox"
|
<Checkbox
|
||||||
name="placement"
|
checked={data.placements === "mabes"} // Automatically checks if placement matches
|
||||||
value="Nasional"
|
disabled // To reflect read-only behavior
|
||||||
/>
|
/>
|
||||||
Nasional
|
<span>Nasional</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* Polda Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="Wilayah"
|
checked={data.placements === "polda"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
Wilayah
|
<span>Wilayah</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* International Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="International"
|
checked={data.placements === "international"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
International
|
<span>International</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -432,110 +619,184 @@ export default function DetailAudio() {
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label className="text-xl text-black">Berikan Komentar</Label>
|
<Label className="text-xl text-black">Berikan Komentar</Label>
|
||||||
<div className="flex items-start gap-3">
|
<div className="mt-4 border p-4 rounded bg-gray-50">
|
||||||
<Avatar>
|
<Textarea
|
||||||
<AvatarImage
|
placeholder="Tulis tanggapan Anda di sini..."
|
||||||
src="/images/avatar/avatar-1.png"
|
value={message}
|
||||||
alt="@shadcn"
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Avatar>
|
<div className="flex justify-end mt-3">
|
||||||
|
<Button
|
||||||
<textarea
|
color="primary"
|
||||||
className="flex-grow p-2 border rounded-lg focus:outline-none focus:border-yellow-400"
|
onClick={() => postData()}
|
||||||
placeholder="Tuliskan komentar Anda di sini.."
|
type="button"
|
||||||
></textarea>
|
>
|
||||||
|
Kirim Komentar
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center mt-2 gap-3">
|
{listData?.map((item: any) => (
|
||||||
<button className="flex items-center text-gray-600 hover:text-gray-800">
|
<div key={item.id} className="flex flex-col gap-3 mt-2 ">
|
||||||
<PaperclipIcon className="w-5 h-5" />
|
<div className="flex flex-row gap-3">
|
||||||
Lampirkan
|
<Avatar className="mt-2">
|
||||||
</button>
|
<AvatarImage
|
||||||
<button className="flex items-center text-gray-600 hover:text-gray-800">
|
src={"/assets/avatar-profile.png"}
|
||||||
<SmileIcon className="w-5 h-5" />
|
alt={`@${item.username}`}
|
||||||
Emoticon
|
/>
|
||||||
</button>
|
</Avatar>
|
||||||
<button className="ml-auto px-4 py-1 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600">
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
Kirim
|
<div className="flex items-center justify-between">
|
||||||
</button>
|
<span className="text-gray-700 font-semibold">
|
||||||
</div>
|
{item.messageFrom.fullname}
|
||||||
</div>
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">
|
||||||
<div className="mt-5">
|
{formatDate(item.createdAt)}
|
||||||
<Label className="text-xl text-black">Komentar</Label>
|
</span>
|
||||||
{commentsData.map((comment) => (
|
</div>
|
||||||
<div
|
<p className="text-gray-800 mt-1">{item.message}</p>
|
||||||
key={comment.id}
|
<div className="flex flex-row gap-2">
|
||||||
className="flex items-start gap-3 mt-2"
|
{/* <div
|
||||||
>
|
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
||||||
<Avatar>
|
onClick={() => handleReply(item.id)}
|
||||||
<AvatarImage
|
>
|
||||||
src={comment.avatar}
|
<DotSquare className="w-4 h-4" />
|
||||||
alt={`@${comment.username}`}
|
<span className="ml-1">Balas</span>
|
||||||
/>
|
</div> */}
|
||||||
</Avatar>
|
<div
|
||||||
<div className="flex flex-col">
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
<span className="text-gray-700 font-semibold">
|
onClick={() => deleteData(item.id)}
|
||||||
{comment.username}
|
>
|
||||||
</span>
|
<TrashIcon className="w-4 h-4" />
|
||||||
<span className="text-gray-500 text-sm">
|
<span className="ml-1">Delete</span>
|
||||||
{comment.date}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
<p className="text-gray-800 mt-1">{comment.text}</p>
|
|
||||||
<div
|
|
||||||
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
|
||||||
onClick={() => handleReply(comment.id)}
|
|
||||||
>
|
|
||||||
<DotSquare className="w-4 h-4" />
|
|
||||||
<span className="ml-1">Balas</span>
|
|
||||||
</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>
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -35,6 +46,11 @@ import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
|
||||||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||||
import JoditEditor from "jodit-react";
|
import JoditEditor from "jodit-react";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { htmlToString } from "@/utils/globals";
|
||||||
|
import { loading } from "@/lib/swal";
|
||||||
|
import { formatDate } from "@fullcalendar/core/index.js";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
|
||||||
const detailSchema = z.object({
|
const detailSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -51,6 +67,13 @@ type Category = {
|
||||||
categoryName: string;
|
categoryName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PublishedForObject = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
isInternal: boolean;
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type curationDetail = {
|
export type curationDetail = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -71,6 +94,13 @@ export type curationDetail = {
|
||||||
userGroupId: number;
|
userGroupId: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
publishedFor: string; // ID for selected radio button
|
||||||
|
publishedForObject: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
isInternal: boolean;
|
||||||
|
code: string;
|
||||||
|
}[];
|
||||||
tags: string;
|
tags: string;
|
||||||
provinceId: string;
|
provinceId: string;
|
||||||
is_active: string;
|
is_active: string;
|
||||||
|
|
@ -109,7 +139,8 @@ export default function DetailDocument() {
|
||||||
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");
|
||||||
|
|
@ -125,6 +156,7 @@ export default function DetailDocument() {
|
||||||
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 [selectedValue, setSelectedValue] = useState<string>("");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|
@ -138,11 +170,18 @@ export default function DetailDocument() {
|
||||||
const [commentsData, setCommentsData] = useState(initialComments);
|
const [commentsData, setCommentsData] = useState(initialComments);
|
||||||
const [replyText, setReplyText] = useState("");
|
const [replyText, setReplyText] = useState("");
|
||||||
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
||||||
|
const [selectedFileId, setSelectedFileId] = useState(null);
|
||||||
|
const [listData, setListData] = useState([]);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
const handleReply = (commentId: number) => {
|
const handleReply = (commentId: number) => {
|
||||||
setReplyingTo(commentId);
|
setReplyingTo(commentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setMessage(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
const addReply = (commentId: number) => {
|
const addReply = (commentId: number) => {
|
||||||
if (replyText.trim()) {
|
if (replyText.trim()) {
|
||||||
const newCommentData = commentsData.map((comment: any) => {
|
const newCommentData = commentsData.map((comment: any) => {
|
||||||
|
|
@ -168,6 +207,85 @@ export default function DetailDocument() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
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 = 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
@ -175,16 +293,37 @@ export default function DetailDocument() {
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
|
setSelectedValue(details?.publishedFor || "");
|
||||||
|
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,
|
||||||
);
|
url: file.secondaryUrl || "default-image.jpg",
|
||||||
|
format: file.format,
|
||||||
|
fileName: file.fileName,
|
||||||
|
placements: file.placements || "",
|
||||||
|
}));
|
||||||
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValueChange = (value: string) => {
|
||||||
|
setSelectedValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-10">
|
<div className="flex gap-10">
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -244,7 +383,7 @@ export default function DetailDocument() {
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Textarea
|
<Textarea
|
||||||
value={detail.description}
|
value={htmlToString(detail.description)}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
placeholder="Enter Meta"
|
placeholder="Enter Meta"
|
||||||
cols={5}
|
cols={5}
|
||||||
|
|
@ -265,24 +404,25 @@ export default function DetailDocument() {
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>Jenis Penugasan</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
// value={type} // State yang dipetakan ke value RadioGroup
|
value={selectedValue} // Set selected value
|
||||||
// onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={handleValueChange} // Update state on change
|
||||||
className="flex flex-wrap gap-3"
|
className="flex flex-wrap gap-3"
|
||||||
>
|
>
|
||||||
|
{/* Static list of radio buttons */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="1" id="publication" />
|
<RadioGroupItem value="5" id="umum" />
|
||||||
<Label htmlFor="umum">Umum</Label>
|
<Label htmlFor="umum">Umum</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="2" id="amplification" />
|
<RadioGroupItem value="8" id="ksp" />
|
||||||
<Label htmlFor="ksp">Ksp</Label>
|
<Label htmlFor="ksp">Ksp</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="3" id="contra" />
|
<RadioGroupItem value="6" id="journalist" />
|
||||||
<Label htmlFor="journalist">Journalist</Label>
|
<Label htmlFor="journalist">Journalist</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="4" id="contra" />
|
<RadioGroupItem value="7" id="polri" />
|
||||||
<Label htmlFor="polri">Polri</Label>
|
<Label htmlFor="polri">Polri</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
@ -330,18 +470,90 @@ export default function DetailDocument() {
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
<Label className="text-xl text-black">File Media</Label>
|
<Label className="text-xl text-black">File Media</Label>
|
||||||
<div className="w-full ">
|
<div className="w-full ">
|
||||||
<Controller
|
<Swiper
|
||||||
control={control}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
name="description"
|
modules={[FreeMode, Navigation, Thumbs]}
|
||||||
render={({ field: { onChange, value } }) => (
|
navigation={false}
|
||||||
<JoditEditor
|
className="w-full"
|
||||||
ref={editor}
|
>
|
||||||
value={detail?.description}
|
{detailThumb?.map((data: any) => (
|
||||||
onChange={onChange}
|
<SwiperSlide
|
||||||
className="dark:text-black"
|
key={data.id}
|
||||||
/>
|
onClick={() => handleFileClick(data.id)}
|
||||||
)}
|
>
|
||||||
/>
|
{[".jpg", ".jpeg", ".png", ".webp"].includes(
|
||||||
|
data.format
|
||||||
|
) ? (
|
||||||
|
// Menampilkan gambar
|
||||||
|
<img
|
||||||
|
className="object-fill h-full w-full rounded-md"
|
||||||
|
src={data.url}
|
||||||
|
alt={data.fileName || "File"}
|
||||||
|
/>
|
||||||
|
) : data.format === ".pdf" ? (
|
||||||
|
// Menampilkan PDF menggunakan iframe
|
||||||
|
<iframe
|
||||||
|
className="w-full h-96 rounded-md"
|
||||||
|
src={data.url}
|
||||||
|
title={data.fileName || "PDF File"}
|
||||||
|
/>
|
||||||
|
) : [".docx", ".ppt", ".pptx"].includes(
|
||||||
|
data.format
|
||||||
|
) ? (
|
||||||
|
// Menampilkan file dokumen menggunakan Office Viewer
|
||||||
|
<iframe
|
||||||
|
className="w-full h-96 rounded-md"
|
||||||
|
src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
|
||||||
|
data.url
|
||||||
|
)}`}
|
||||||
|
title={data.fileName || "Document"}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
// Menampilkan link jika format tidak dikenali
|
||||||
|
<a
|
||||||
|
href={data.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block text-blue-500 underline"
|
||||||
|
>
|
||||||
|
View {data.fileName || "File"}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
<div className="mt-2">
|
||||||
|
<Swiper
|
||||||
|
onSwiper={setThumbsSwiper}
|
||||||
|
slidesPerView={6}
|
||||||
|
spaceBetween={8}
|
||||||
|
pagination={{ clickable: true }}
|
||||||
|
modules={[Pagination, Thumbs]}
|
||||||
|
>
|
||||||
|
{detailThumb?.map((data: any) => (
|
||||||
|
<SwiperSlide
|
||||||
|
key={data.id}
|
||||||
|
onClick={() => handleFileClick(data.id)}
|
||||||
|
>
|
||||||
|
{[".jpg", ".jpeg", ".png", ".webp"].includes(
|
||||||
|
data.format
|
||||||
|
) ? (
|
||||||
|
<img
|
||||||
|
className="object-cover h-[60px] w-[80px]"
|
||||||
|
src={"/assets/docx-icon.jpg"}
|
||||||
|
alt={data.fileName}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="h-[60px] w-[80px] flex items-center justify-center bg-gray-200 text-sm text-center text-gray-700 rounded-md">
|
||||||
|
{data?.format
|
||||||
|
?.replace(".", "")
|
||||||
|
.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -357,45 +569,42 @@ export default function DetailDocument() {
|
||||||
<p>Penempatan</p>
|
<p>Penempatan</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="bg-black h-1 w-full rounded-lg"></p>
|
<p className="bg-black h-1 w-full rounded-lg"></p>
|
||||||
{detailThumb?.map((data: any) => (
|
{detailThumb.map((data: any, index: number) => (
|
||||||
<div
|
<div
|
||||||
key={data.id}
|
key={index}
|
||||||
className="flex items-center gap-3 mt-2"
|
className="flex items-center gap-3 mt-2"
|
||||||
>
|
>
|
||||||
{/* <img
|
|
||||||
className="object-cover w-20 h-20"
|
|
||||||
src={data.thumbnailUrl} // Assuming `thumbnailUrl` is the property that contains the URL for the thumbnail image
|
|
||||||
alt={`Thumbnail ${index}`}
|
|
||||||
/> */}
|
|
||||||
<img
|
<img
|
||||||
className="object-cover w-36 h-32"
|
className="object-cover h-[60px] w-[80px]"
|
||||||
src={data}
|
src={"/assets/docx-icon.jpg"}
|
||||||
alt={` ${data.id}`}
|
alt={data.fileName}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row gap-3 items-center">
|
<div className="flex flex-row gap-3 items-center">
|
||||||
<label className="text-blue-500 cursor-pointer">
|
{/* Mabes Checkbox */}
|
||||||
<input
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
type="checkbox"
|
<Checkbox
|
||||||
name="placement"
|
checked={data.placements === "mabes"} // Automatically checks if placement matches
|
||||||
value="Nasional"
|
disabled // To reflect read-only behavior
|
||||||
/>
|
/>
|
||||||
Nasional
|
<span>Nasional</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* Polda Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="Wilayah"
|
checked={data.placements === "polda"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
Wilayah
|
<span>Wilayah</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* International Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="International"
|
checked={data.placements === "international"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
International
|
<span>International</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -409,110 +618,184 @@ export default function DetailDocument() {
|
||||||
<div className="gap-5 mb-5">
|
<div className="gap-5 mb-5">
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label className="text-xl text-black">Berikan Komentar</Label>
|
<Label className="text-xl text-black">Berikan Komentar</Label>
|
||||||
<div className="flex items-start gap-3">
|
<div className="mt-4 border p-4 rounded bg-gray-50">
|
||||||
<Avatar>
|
<Textarea
|
||||||
<AvatarImage
|
placeholder="Tulis tanggapan Anda di sini..."
|
||||||
src="/images/avatar/avatar-1.png"
|
value={message}
|
||||||
alt="@shadcn"
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Avatar>
|
<div className="flex justify-end mt-3">
|
||||||
|
<Button
|
||||||
<textarea
|
color="primary"
|
||||||
className="flex-grow p-2 border rounded-lg focus:outline-none focus:border-yellow-400"
|
onClick={() => postData()}
|
||||||
placeholder="Tuliskan komentar Anda di sini.."
|
type="button"
|
||||||
></textarea>
|
>
|
||||||
|
Kirim Komentar
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center mt-2 gap-3">
|
{listData?.map((item: any) => (
|
||||||
<button className="flex items-center text-gray-600 hover:text-gray-800">
|
<div key={item.id} className="flex flex-col gap-3 mt-2 ">
|
||||||
<PaperclipIcon className="w-5 h-5" />
|
<div className="flex flex-row gap-3">
|
||||||
Lampirkan
|
<Avatar className="mt-2">
|
||||||
</button>
|
<AvatarImage
|
||||||
<button className="flex items-center text-gray-600 hover:text-gray-800">
|
src={"/assets/avatar-profile.png"}
|
||||||
<SmileIcon className="w-5 h-5" />
|
alt={`@${item.username}`}
|
||||||
Emoticon
|
/>
|
||||||
</button>
|
</Avatar>
|
||||||
<button className="ml-auto px-4 py-1 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600">
|
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
|
||||||
Kirim
|
<div className="flex items-center justify-between">
|
||||||
</button>
|
<span className="text-gray-700 font-semibold">
|
||||||
</div>
|
{item.messageFrom.fullname}
|
||||||
</div>
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">
|
||||||
<div className="mt-5">
|
{formatDate(item.createdAt)}
|
||||||
<Label className="text-xl text-black">Komentar</Label>
|
</span>
|
||||||
{commentsData.map((comment) => (
|
</div>
|
||||||
<div
|
<p className="text-gray-800 mt-1">{item.message}</p>
|
||||||
key={comment.id}
|
<div className="flex flex-row gap-2">
|
||||||
className="flex items-start gap-3 mt-2"
|
{/* <div
|
||||||
>
|
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
||||||
<Avatar>
|
onClick={() => handleReply(item.id)}
|
||||||
<AvatarImage
|
>
|
||||||
src={comment.avatar}
|
<DotSquare className="w-4 h-4" />
|
||||||
alt={`@${comment.username}`}
|
<span className="ml-1">Balas</span>
|
||||||
/>
|
</div> */}
|
||||||
</Avatar>
|
<div
|
||||||
<div className="flex flex-col">
|
className="flex items-center mt-1 text-red-500 cursor-pointer"
|
||||||
<span className="text-gray-700 font-semibold">
|
onClick={() => deleteData(item.id)}
|
||||||
{comment.username}
|
>
|
||||||
</span>
|
<TrashIcon className="w-4 h-4" />
|
||||||
<span className="text-gray-500 text-sm">
|
<span className="ml-1">Delete</span>
|
||||||
{comment.date}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
<p className="text-gray-800 mt-1">{comment.text}</p>
|
|
||||||
<div
|
|
||||||
className="flex items-center mt-1 text-blue-500 cursor-pointer"
|
|
||||||
onClick={() => handleReply(comment.id)}
|
|
||||||
>
|
|
||||||
<DotSquare className="w-4 h-4" />
|
|
||||||
<span className="ml-1">Balas</span>
|
|
||||||
</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>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
CarouselNext,
|
CarouselNext,
|
||||||
CarouselPrevious,
|
CarouselPrevious,
|
||||||
} from "@/components/ui/carousel";
|
} from "@/components/ui/carousel";
|
||||||
|
import { listCuratedContent } from "@/service/curated-content/curated-content";
|
||||||
import { getListContent } from "@/service/landing/landing";
|
import { getListContent } from "@/service/landing/landing";
|
||||||
import {
|
import {
|
||||||
formatDateToIndonesian,
|
formatDateToIndonesian,
|
||||||
|
|
@ -21,10 +22,12 @@ const TeksSliderPage = () => {
|
||||||
const [documentData, setDocumentData] = useState<any>();
|
const [documentData, setDocumentData] = useState<any>();
|
||||||
const [displayDocument, setDisplayDocument] = useState<any[]>([]);
|
const [displayDocument, setDisplayDocument] = 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();
|
initFetch();
|
||||||
}, []);
|
}, [page, limit, search]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (documentData?.length > 0) {
|
if (documentData?.length > 0) {
|
||||||
|
|
@ -35,14 +38,12 @@ const TeksSliderPage = () => {
|
||||||
}, [documentData]);
|
}, [documentData]);
|
||||||
|
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const response = await getListContent({
|
const response = await listCuratedContent(search, limit, page - 1, 3, "1");
|
||||||
page: page - 1,
|
|
||||||
size: 12,
|
|
||||||
sortBy: "createdAt",
|
|
||||||
contentTypeId: "3",
|
|
||||||
});
|
|
||||||
console.log(response);
|
console.log(response);
|
||||||
setDocumentData(response?.data?.data?.content);
|
|
||||||
|
const data = response?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
setDocumentData(contentData);
|
||||||
};
|
};
|
||||||
const shuffleAndSetVideos = () => {
|
const shuffleAndSetVideos = () => {
|
||||||
const shuffled = shuffleArray([...documentData]);
|
const shuffled = shuffleArray([...documentData]);
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ import {
|
||||||
} from "@/service/task";
|
} from "@/service/task";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, loading } from "@/lib/swal";
|
import { close, loading } from "@/lib/swal";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { htmlToString } from "@/utils/globals";
|
||||||
|
|
||||||
const detailSchema = z.object({
|
const detailSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -107,6 +109,13 @@ export type curationDetail = {
|
||||||
userGroupId: number;
|
userGroupId: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
publishedFor: string; // ID for selected radio button
|
||||||
|
publishedForObject: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
isInternal: boolean;
|
||||||
|
code: string;
|
||||||
|
}[];
|
||||||
tags: string;
|
tags: string;
|
||||||
provinceId: string;
|
provinceId: string;
|
||||||
is_active: string;
|
is_active: string;
|
||||||
|
|
@ -173,6 +182,7 @@ export default function DetailImage() {
|
||||||
const [commentsData, setCommentsData] = useState(initialComments);
|
const [commentsData, setCommentsData] = useState(initialComments);
|
||||||
const [replyText, setReplyText] = useState("");
|
const [replyText, setReplyText] = useState("");
|
||||||
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
||||||
|
const [selectedValue, setSelectedValue] = useState<string>("");
|
||||||
|
|
||||||
const handleReply = (commentId: number) => {
|
const handleReply = (commentId: number) => {
|
||||||
setReplyingTo(commentId);
|
setReplyingTo(commentId);
|
||||||
|
|
@ -272,11 +282,13 @@ export default function DetailImage() {
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
|
setSelectedValue(details?.publishedFor || "");
|
||||||
setSelectedFileId(details?.files[0]?.id);
|
setSelectedFileId(details?.files[0]?.id);
|
||||||
const filesData = details.files || [];
|
const filesData = details.files || [];
|
||||||
const fileUrls = filesData.map((file: any) => ({
|
const fileUrls = filesData.map((file: any) => ({
|
||||||
id: file.id,
|
id: file.id,
|
||||||
thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg",
|
thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg",
|
||||||
|
placements: file.placements || "",
|
||||||
}));
|
}));
|
||||||
setDetailThumb(fileUrls);
|
setDetailThumb(fileUrls);
|
||||||
}
|
}
|
||||||
|
|
@ -294,6 +306,9 @@ export default function DetailImage() {
|
||||||
console.error("Error fetching comments:", error);
|
console.error("Error fetching comments:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleValueChange = (value: string) => {
|
||||||
|
setSelectedValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-10">
|
<div className="flex gap-10">
|
||||||
|
|
@ -354,7 +369,7 @@ export default function DetailImage() {
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Textarea
|
<Textarea
|
||||||
value={detail.description}
|
value={htmlToString(detail.description)}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
placeholder="Enter Meta"
|
placeholder="Enter Meta"
|
||||||
cols={5}
|
cols={5}
|
||||||
|
|
@ -375,24 +390,25 @@ export default function DetailImage() {
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>Jenis Penugasan</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
// value={type} // State yang dipetakan ke value RadioGroup
|
value={selectedValue} // Set selected value
|
||||||
// onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={handleValueChange} // Update state on change
|
||||||
className="flex flex-wrap gap-3"
|
className="flex flex-wrap gap-3"
|
||||||
>
|
>
|
||||||
|
{/* Static list of radio buttons */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="1" id="publication" />
|
<RadioGroupItem value="5" id="umum" />
|
||||||
<Label htmlFor="umum">Umum</Label>
|
<Label htmlFor="umum">Umum</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="2" id="amplification" />
|
<RadioGroupItem value="8" id="ksp" />
|
||||||
<Label htmlFor="ksp">Ksp</Label>
|
<Label htmlFor="ksp">Ksp</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="3" id="contra" />
|
<RadioGroupItem value="6" id="journalist" />
|
||||||
<Label htmlFor="journalist">Journalist</Label>
|
<Label htmlFor="journalist">Journalist</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="4" id="contra" />
|
<RadioGroupItem value="7" id="polri" />
|
||||||
<Label htmlFor="polri">Polri</Label>
|
<Label htmlFor="polri">Polri</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
@ -452,7 +468,7 @@ export default function DetailImage() {
|
||||||
onClick={() => handleFileClick(data.id)}
|
onClick={() => handleFileClick(data.id)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="object-fill h-full w-full"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data.thumbnailFileUrl}
|
src={data.thumbnailFileUrl}
|
||||||
alt={`File ID: ${data.id}`}
|
alt={`File ID: ${data.id}`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -476,7 +492,7 @@ export default function DetailImage() {
|
||||||
onClick={() => handleFileClick(data.id)}
|
onClick={() => handleFileClick(data.id)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="object-fill h-full w-full"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data.thumbnailFileUrl}
|
src={data.thumbnailFileUrl}
|
||||||
alt={`File ID: ${data.id}`}
|
alt={`File ID: ${data.id}`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -510,34 +526,36 @@ export default function DetailImage() {
|
||||||
alt={`Thumbnail ${index}`}
|
alt={`Thumbnail ${index}`}
|
||||||
/> */}
|
/> */}
|
||||||
<img
|
<img
|
||||||
className="object-cover w-36 h-32"
|
className="object-cover w-36 h-32 rounded-md"
|
||||||
src={data}
|
src={data.thumbnailFileUrl}
|
||||||
alt={`Article ${data.id}`}
|
alt={`Article ${data.id}`}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row gap-3 items-center">
|
<div className="flex flex-row gap-3 items-center">
|
||||||
<label className="text-blue-500 cursor-pointer">
|
{/* Mabes Checkbox */}
|
||||||
<input
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
type="checkbox"
|
<Checkbox
|
||||||
name="placement"
|
checked={data.placements === "mabes"} // Automatically checks if placement matches
|
||||||
value="Nasional"
|
disabled // To reflect read-only behavior
|
||||||
/>
|
/>
|
||||||
Nasional
|
<span>Nasional</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* Polda Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="Wilayah"
|
checked={data.placements === "polda"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
Wilayah
|
<span>Wilayah</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* International Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="International"
|
checked={data.placements === "international"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
International
|
<span>International</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -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,11 @@ 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 { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { formatDate } from "@fullcalendar/core/index.js";
|
||||||
|
import { loading } from "@/lib/swal";
|
||||||
|
import { htmlToString } from "@/utils/globals";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
|
||||||
const detailSchema = z.object({
|
const detailSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -70,6 +86,13 @@ export type curationDetail = {
|
||||||
userGroupId: number;
|
userGroupId: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
publishedFor: string; // ID for selected radio button
|
||||||
|
publishedForObject: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
isInternal: boolean;
|
||||||
|
code: string;
|
||||||
|
}[];
|
||||||
tags: string;
|
tags: string;
|
||||||
provinceId: string;
|
provinceId: string;
|
||||||
is_active: string;
|
is_active: string;
|
||||||
|
|
@ -108,7 +131,7 @@ 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 [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");
|
||||||
|
|
@ -125,6 +148,7 @@ export default function DetailImage() {
|
||||||
const [detailVideo, setDetailVideo] = useState<any>([]);
|
const [detailVideo, setDetailVideo] = useState<any>([]);
|
||||||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
const [selectedValue, setSelectedValue] = useState<string>("");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|
@ -138,59 +162,156 @@ export default function DetailImage() {
|
||||||
const [commentsData, setCommentsData] = useState(initialComments);
|
const [commentsData, setCommentsData] = useState(initialComments);
|
||||||
const [replyText, setReplyText] = useState("");
|
const [replyText, setReplyText] = useState("");
|
||||||
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
const [replyingTo, setReplyingTo] = useState<number | null>(null);
|
||||||
|
const [selectedFileId, setSelectedFileId] = useState(null);
|
||||||
|
const [listData, setListData] = useState([]);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
const handleReply = (commentId: number) => {
|
const handleReply = (commentId: number) => {
|
||||||
setReplyingTo(commentId);
|
setReplyingTo(commentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReply = (commentId: number) => {
|
useEffect(() => {
|
||||||
if (replyText.trim()) {
|
async function initState() {
|
||||||
const newCommentData = commentsData.map((comment: any) => {
|
// loading();
|
||||||
if (comment.id === commentId) {
|
const response = await getMediaCurationMessage(selectedFileId);
|
||||||
return {
|
console.log("data", response?.data?.data);
|
||||||
...comment,
|
console.log("userLvl", userLevelNumber);
|
||||||
replies: [
|
setListData(response?.data?.data);
|
||||||
...comment.replies,
|
close();
|
||||||
{
|
}
|
||||||
text: replyText,
|
|
||||||
username: "You",
|
|
||||||
date: new Date().toLocaleString(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return comment;
|
|
||||||
});
|
|
||||||
|
|
||||||
setCommentsData(newCommentData);
|
initState();
|
||||||
setReplyText("");
|
}, [selectedFileId]);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setMessage(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// 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]);
|
||||||
|
|
||||||
|
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) {
|
||||||
const response = await detailMedia(id);
|
const response = await detailMedia(id);
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
|
||||||
setDetail(details);
|
|
||||||
const filesData = details.files || [];
|
const filesData = details.files || [];
|
||||||
const fileUrls = filesData.map((file: { url: string }) =>
|
const fileUrls = filesData.map((file: any) => ({
|
||||||
file.url ? file.url : "default-image.jpg"
|
id: file.id,
|
||||||
);
|
url: file.url || "default-image.jpg",
|
||||||
|
placements: file.placements || "",
|
||||||
|
}));
|
||||||
|
setDetail(details);
|
||||||
|
setSelectedValue(details?.publishedFor || "");
|
||||||
|
setSelectedFileId(details?.files[0]?.id);
|
||||||
setDetailVideo(fileUrls);
|
setDetailVideo(fileUrls);
|
||||||
const filesDataThumbnail = details.files || [];
|
const filesDataThumbnail = details.files || [];
|
||||||
const fileUrlsThumbnail = filesDataThumbnail.map(
|
const fileUrlsThumbnail = filesData.map(
|
||||||
(file: { thumbnailFileUrl: string }) =>
|
(file: { thumbnailFileUrl: string; placements: string }) => ({
|
||||||
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
|
thumbnailFileUrl: file.thumbnailFileUrl
|
||||||
|
? file.thumbnailFileUrl
|
||||||
|
: "default-image.jpg",
|
||||||
|
placements: file.placements || "",
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
setDetailThumb(fileUrlsThumbnail);
|
setDetailThumb(fileUrlsThumbnail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValueChange = (value: string) => {
|
||||||
|
setSelectedValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-10">
|
<div className="flex gap-10">
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -250,7 +371,7 @@ export default function DetailImage() {
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Textarea
|
<Textarea
|
||||||
value={detail.description}
|
value={htmlToString(detail.description)}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
placeholder="Enter Meta"
|
placeholder="Enter Meta"
|
||||||
cols={5}
|
cols={5}
|
||||||
|
|
@ -271,24 +392,25 @@ export default function DetailImage() {
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Label>Jenis Penugasan</Label>
|
<Label>Jenis Penugasan</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
// value={type} // State yang dipetakan ke value RadioGroup
|
value={selectedValue} // Set selected value
|
||||||
// onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
|
onValueChange={handleValueChange} // Update state on change
|
||||||
className="flex flex-wrap gap-3"
|
className="flex flex-wrap gap-3"
|
||||||
>
|
>
|
||||||
|
{/* Static list of radio buttons */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="1" id="publication" />
|
<RadioGroupItem value="5" id="umum" />
|
||||||
<Label htmlFor="umum">Umum</Label>
|
<Label htmlFor="umum">Umum</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="2" id="amplification" />
|
<RadioGroupItem value="8" id="ksp" />
|
||||||
<Label htmlFor="ksp">Ksp</Label>
|
<Label htmlFor="ksp">Ksp</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="3" id="contra" />
|
<RadioGroupItem value="6" id="journalist" />
|
||||||
<Label htmlFor="journalist">Journalist</Label>
|
<Label htmlFor="journalist">Journalist</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RadioGroupItem value="4" id="contra" />
|
<RadioGroupItem value="7" id="polri" />
|
||||||
<Label htmlFor="polri">Polri</Label>
|
<Label htmlFor="polri">Polri</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
@ -343,10 +465,13 @@ export default function DetailImage() {
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{detailVideo?.map((data: any) => (
|
{detailVideo?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide
|
||||||
|
key={data.id}
|
||||||
|
onClick={() => handleFileClick(data.id)}
|
||||||
|
>
|
||||||
<video
|
<video
|
||||||
className="object-fill h-full w-full"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data}
|
src={data.url}
|
||||||
controls
|
controls
|
||||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||||
/>
|
/>
|
||||||
|
|
@ -364,10 +489,13 @@ export default function DetailImage() {
|
||||||
modules={[Pagination, Thumbs]}
|
modules={[Pagination, Thumbs]}
|
||||||
>
|
>
|
||||||
{detailVideo?.map((data: any) => (
|
{detailVideo?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide
|
||||||
|
key={data.id}
|
||||||
|
onClick={() => handleFileClick(data.id)}
|
||||||
|
>
|
||||||
<video
|
<video
|
||||||
className="object-cover h-[60px] w-[80px]"
|
className="object-cover h-[60px] w-[80px] rounded-md"
|
||||||
src={data}
|
src={data.url}
|
||||||
muted
|
muted
|
||||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||||
/>
|
/>
|
||||||
|
|
@ -401,34 +529,36 @@ export default function DetailImage() {
|
||||||
alt={`Thumbnail ${index}`}
|
alt={`Thumbnail ${index}`}
|
||||||
/> */}
|
/> */}
|
||||||
<img
|
<img
|
||||||
className="object-cover w-36 h-32"
|
className="object-cover w-32 h-32"
|
||||||
src={data}
|
src={"/assets/video-icon.webp"}
|
||||||
alt={` ${data.id}`}
|
alt={` ${data.id}`}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row gap-3 items-center">
|
<div className="flex flex-row gap-3 items-center">
|
||||||
<label className="text-blue-500 cursor-pointer">
|
{/* Mabes Checkbox */}
|
||||||
<input
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
type="checkbox"
|
<Checkbox
|
||||||
name="placement"
|
checked={data.placements === "mabes"} // Automatically checks if placement matches
|
||||||
value="Nasional"
|
disabled // To reflect read-only behavior
|
||||||
/>
|
/>
|
||||||
Nasional
|
<span>Nasional</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* Polda Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="Wilayah"
|
checked={data.placements === "polda"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
Wilayah
|
<span>Wilayah</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-blue-500 cursor-pointer">
|
|
||||||
<input
|
{/* International Checkbox */}
|
||||||
type="checkbox"
|
<label className=" cursor-pointer flex items-center gap-2">
|
||||||
name="placement"
|
<Checkbox
|
||||||
value="International"
|
checked={data.placements === "international"} // Automatically checks if placement matches
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
International
|
<span>International</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -440,112 +570,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>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Rows, Search, UploadIcon } from "lucide-react";
|
import { ArrowRight, Rows, Search, UploadIcon } from "lucide-react";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
@ -76,65 +76,76 @@ const CuratedContentPage = () => {
|
||||||
<div className="flex justify-between items-center mx-3">
|
<div className="flex justify-between items-center mx-3">
|
||||||
<Label className="text-base">Audio Visual</Label>
|
<Label className="text-base">Audio Visual</Label>
|
||||||
|
|
||||||
<Label>
|
<div className="flex items-center">
|
||||||
{" "}
|
<Label>
|
||||||
<Link
|
{" "}
|
||||||
href={
|
<Link
|
||||||
"/shared/curated-content/giat-routine/video/all"
|
href={
|
||||||
}
|
"/shared/curated-content/giat-routine/video/all"
|
||||||
>
|
}
|
||||||
Lihat Semua
|
>
|
||||||
</Link>
|
Lihat Semua
|
||||||
</Label>
|
</Link>
|
||||||
|
</Label>
|
||||||
|
<ArrowRight size={18} className="text-slate-600" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<VideoSliderPage />
|
<VideoSliderPage />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center mx-3">
|
<div className="flex justify-between items-center mx-3">
|
||||||
<Label className="text-base">Audio</Label>
|
<Label className="text-base">Audio</Label>
|
||||||
|
<div className="flex items-center">
|
||||||
<Label>
|
<Label>
|
||||||
{" "}
|
{" "}
|
||||||
<Link
|
<Link
|
||||||
href={
|
href={
|
||||||
"/shared/curated-content/giat-routine/audio/all"
|
"/shared/curated-content/giat-routine/audio/all"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Lihat Semua
|
Lihat Semua
|
||||||
</Link>
|
</Link>
|
||||||
</Label>
|
</Label>
|
||||||
|
<ArrowRight size={18} className="text-slate-600" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<AudioSliderPage />
|
<AudioSliderPage />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center mx-3">
|
<div className="flex justify-between items-center mx-3">
|
||||||
<Label className="text-base">Foto</Label>
|
<Label className="text-base">Foto</Label>
|
||||||
|
<div className="flex items-center">
|
||||||
<Label>
|
<Label>
|
||||||
{" "}
|
{" "}
|
||||||
<Link
|
<Link
|
||||||
href={
|
href={
|
||||||
"/shared/curated-content/giat-routine/image/all"
|
"/shared/curated-content/giat-routine/image/all"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Lihat Semua
|
Lihat Semua
|
||||||
</Link>
|
</Link>
|
||||||
</Label>
|
</Label>
|
||||||
|
<ArrowRight size={18} className="text-slate-600" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<ImageSliderPage />
|
<ImageSliderPage />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center mx-3">
|
<div className="flex justify-between items-center mx-3">
|
||||||
<Label className="text-base">Teks</Label>
|
<Label className="text-base">Teks</Label>
|
||||||
<Label>
|
<div className="flex items-center">
|
||||||
<Link
|
<Label>
|
||||||
href={
|
{" "}
|
||||||
"/shared/curated-content/giat-routine/document/all"
|
<Link
|
||||||
}
|
href={
|
||||||
>
|
"/shared/curated-content/giat-routine/document/all"
|
||||||
Lihat Semua
|
}
|
||||||
</Link>
|
>
|
||||||
</Label>
|
Lihat Semua
|
||||||
|
</Link>
|
||||||
|
</Label>
|
||||||
|
<ArrowRight size={18} className="text-slate-600" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-5 my-5">
|
<div className="px-5 my-5">
|
||||||
<TeksSliderPage />
|
<TeksSliderPage />
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import {
|
||||||
} from "@/service/content/content";
|
} from "@/service/content/content";
|
||||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { MailIcon } from "lucide-react";
|
import { MailIcon, Music } from "lucide-react";
|
||||||
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";
|
||||||
|
|
@ -138,25 +138,23 @@ export default function FormAudioDetail() {
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||||
const [audioPlaying, setAudioPlaying] = useState<any>(null);
|
const [audioPlaying, setAudioPlaying] = useState<any>(null);
|
||||||
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
|
|
||||||
const waveSurferRef = useRef<any>(null);
|
const waveSurferRef = useRef<any>(null);
|
||||||
|
|
||||||
|
|
||||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
const onReady = (ws: any) => {
|
const onReady = (ws: any) => {
|
||||||
setWavesurfer(ws)
|
setWavesurfer(ws);
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const onPlayPause = () => {
|
const onPlayPause = () => {
|
||||||
wavesurfer && wavesurfer.playPause();
|
wavesurfer && wavesurfer.playPause();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
let fileTypeId = "4";
|
let fileTypeId = "4";
|
||||||
|
|
||||||
|
|
@ -187,7 +185,7 @@ export default function FormAudioDetail() {
|
||||||
userLevelId == "216" &&
|
userLevelId == "216" &&
|
||||||
roleId == "3"
|
roleId == "3"
|
||||||
) {
|
) {
|
||||||
setIsMabesApprover(true);
|
setIsUserMabesApprover(true);
|
||||||
}
|
}
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
|
|
@ -230,6 +228,14 @@ export default function FormAudioDetail() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setupPlacementCheck = (length: number) => {
|
||||||
|
const temp = [];
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
temp.push([]);
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|
@ -244,6 +250,7 @@ export default function FormAudioDetail() {
|
||||||
names: details?.files[0]?.fileName,
|
names: details?.files[0]?.fileName,
|
||||||
format: details?.files[0]?.format,
|
format: details?.files[0]?.format,
|
||||||
});
|
});
|
||||||
|
setupPlacementCheck(details?.files?.length);
|
||||||
|
|
||||||
if (details.publishedForObject) {
|
if (details.publishedForObject) {
|
||||||
const publisherIds = details.publishedForObject.map(
|
const publisherIds = details.publishedForObject.map(
|
||||||
|
|
@ -281,9 +288,16 @@ export default function FormAudioDetail() {
|
||||||
}, [refresh, setValue]);
|
}, [refresh, setValue]);
|
||||||
|
|
||||||
const actionApproval = (e: string) => {
|
const actionApproval = (e: string) => {
|
||||||
|
const temp = [];
|
||||||
|
for (const element of detail.files) {
|
||||||
|
temp.push([]);
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
setStatus(e);
|
setStatus(e);
|
||||||
setModalOpen(true);
|
setFiles(detail.files);
|
||||||
|
|
||||||
setDescription("");
|
setDescription("");
|
||||||
|
setModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
|
|
@ -308,13 +322,26 @@ export default function FormAudioDetail() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPlacement = () => {
|
||||||
|
console.log("getPlaa", filePlacements);
|
||||||
|
const temp = [];
|
||||||
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
|
if (filePlacements[i].length !== 0) {
|
||||||
|
const now = filePlacements[i].filter((a) => a !== "all");
|
||||||
|
const data = { mediaFileId: files[i].id, placement: now.join(",") };
|
||||||
|
temp.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const data = {
|
const data = {
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: [],
|
// files: [],
|
||||||
// files: isMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -337,6 +364,7 @@ export default function FormAudioDetail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
submitApprovalSuccesss();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -348,6 +376,40 @@ export default function FormAudioDetail() {
|
||||||
rejects.push(id);
|
rejects.push(id);
|
||||||
setRejectedFiles(rejects);
|
setRejectedFiles(rejects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setupPlacement = (
|
||||||
|
index: number,
|
||||||
|
placement: string,
|
||||||
|
checked: boolean
|
||||||
|
) => {
|
||||||
|
let temp = [...filePlacements];
|
||||||
|
if (checked) {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
|
} else {
|
||||||
|
const now = temp[index];
|
||||||
|
now.push(placement);
|
||||||
|
if (now.length === 3 && !now.includes("all")) {
|
||||||
|
now.push("all");
|
||||||
|
}
|
||||||
|
temp[index] = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = [];
|
||||||
|
} else {
|
||||||
|
const now = temp[index].filter((a) => a !== placement);
|
||||||
|
console.log("now", now);
|
||||||
|
temp[index] = now;
|
||||||
|
if (now.length === 3 && now.includes("all")) {
|
||||||
|
const newData = now.filter((b) => b !== "all");
|
||||||
|
temp[index] = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
};
|
||||||
|
|
||||||
const handleMain = (
|
const handleMain = (
|
||||||
type: string,
|
type: string,
|
||||||
url: string,
|
url: string,
|
||||||
|
|
@ -380,7 +442,7 @@ export default function FormAudioDetail() {
|
||||||
confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
router.push("/in/contributor/content/image");
|
router.push("/in/contributor/content/audio");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -469,7 +531,27 @@ export default function FormAudioDetail() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<p onClick={onPlayPause}>{isPlaying ? "Pause" : "Play"}</p>
|
<Button
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={onPlayPause}
|
||||||
|
disabled={isPlaying}
|
||||||
|
className={`flex items-center gap-2 ${
|
||||||
|
isPlaying
|
||||||
|
? "bg-gray-300 cursor-not-allowed"
|
||||||
|
: "bg-primary text-white"
|
||||||
|
} p-2 rounded`}
|
||||||
|
>
|
||||||
|
{isPlaying ? "Pause" : "Play"}
|
||||||
|
<Icon
|
||||||
|
icon={
|
||||||
|
isPlaying
|
||||||
|
? "carbon:pause-outline"
|
||||||
|
: "famicons:play-sharp"
|
||||||
|
}
|
||||||
|
className="h-5 w-5"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -609,7 +691,7 @@ export default function FormAudioDetail() {
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>Berikan Komentar</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file) => (
|
? files?.map((file, index) => (
|
||||||
<div
|
<div
|
||||||
key={file.id}
|
key={file.id}
|
||||||
className="flex flex-row gap-2 items-center"
|
className="flex flex-row gap-2 items-center"
|
||||||
|
|
@ -618,49 +700,92 @@ export default function FormAudioDetail() {
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
{file.fileName}
|
{file.fileName}
|
||||||
<a>
|
<a
|
||||||
|
onClick={() =>
|
||||||
|
handleDeleteFileApproval(file.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-2">
|
{isUserMabesApprover && (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex flex-row gap-2">
|
||||||
<Checkbox id="terms" />
|
<div className="flex items-center space-x-2">
|
||||||
<label
|
<Checkbox
|
||||||
htmlFor="terms"
|
id="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
value="all"
|
||||||
>
|
checked={filePlacements[index]?.includes(
|
||||||
Semua
|
"all"
|
||||||
</label>
|
)}
|
||||||
</div>
|
onCheckedChange={(e) =>
|
||||||
<div className="flex items-center space-x-2">
|
setupPlacement(index, "all", Boolean(e))
|
||||||
<Checkbox id="terms" />
|
}
|
||||||
<label
|
/>
|
||||||
htmlFor="terms"
|
<label
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
htmlFor="terms"
|
||||||
>
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
Nasional
|
>
|
||||||
</label>
|
Semua
|
||||||
</div>
|
</label>
|
||||||
<div className="flex items-center space-x-2">
|
</div>
|
||||||
<Checkbox id="terms" />
|
<div className="flex items-center space-x-2">
|
||||||
<label
|
<Checkbox
|
||||||
htmlFor="terms"
|
id="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
checked={filePlacements[index]?.includes(
|
||||||
>
|
"mabes"
|
||||||
Wilayah
|
)}
|
||||||
</label>
|
onCheckedChange={(e) =>
|
||||||
</div>
|
setupPlacement(index, "mabes", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Nasional
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
"polda"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
setupPlacement(index, "polda", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Wilayah
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox id="terms" />
|
<Checkbox
|
||||||
<label
|
id="terms"
|
||||||
htmlFor="terms"
|
checked={filePlacements[index]?.includes(
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
"international"
|
||||||
>
|
)}
|
||||||
Internasional
|
onCheckedChange={(e) =>
|
||||||
</label>
|
setupPlacement(
|
||||||
|
index,
|
||||||
|
"international",
|
||||||
|
Boolean(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Internasional
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
@ -743,14 +868,16 @@ export default function FormAudioDetail() {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={submitApprovalSuccesss}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={() => setModalOpen(false)}
|
onClick={() => {
|
||||||
|
setModalOpen(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -355,10 +355,8 @@ export default function FormConvertSPIT() {
|
||||||
const handleCheckboxChange = (id: string): void => {
|
const handleCheckboxChange = (id: string): void => {
|
||||||
if (id === "all") {
|
if (id === "all") {
|
||||||
if (publishedFor.includes("all")) {
|
if (publishedFor.includes("all")) {
|
||||||
// Uncheck all checkboxes
|
|
||||||
setPublishedFor([]);
|
setPublishedFor([]);
|
||||||
} else {
|
} else {
|
||||||
// Select all checkboxes
|
|
||||||
setPublishedFor(
|
setPublishedFor(
|
||||||
options
|
options
|
||||||
.filter((opt: any) => opt.id !== "all")
|
.filter((opt: any) => opt.id !== "all")
|
||||||
|
|
@ -370,7 +368,6 @@ export default function FormConvertSPIT() {
|
||||||
? publishedFor.filter((item) => item !== id)
|
? publishedFor.filter((item) => item !== id)
|
||||||
: [...publishedFor, id];
|
: [...publishedFor, id];
|
||||||
|
|
||||||
// Remove "all" if any checkbox is unchecked
|
|
||||||
if (publishedFor.includes("all") && id !== "all") {
|
if (publishedFor.includes("all") && id !== "all") {
|
||||||
setPublishedFor(updatedPublishedFor.filter((item) => item !== "all"));
|
setPublishedFor(updatedPublishedFor.filter((item) => item !== "all"));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -416,10 +413,10 @@ export default function FormConvertSPIT() {
|
||||||
description,
|
description,
|
||||||
htmlDescription: description,
|
htmlDescription: description,
|
||||||
tags: "siap",
|
tags: "siap",
|
||||||
categoryId: 1,
|
categoryId: selectedCategoryId,
|
||||||
publishedFor: publishedFor.join(","),
|
publishedFor: publishedFor.join(","),
|
||||||
creator: data.contentCreator,
|
creator: data.contentCreator,
|
||||||
files: isUserMabesApprover ? getPlacement() : [], // Include placement data
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await convertSPIT(requestData);
|
const response = await convertSPIT(requestData);
|
||||||
|
|
@ -698,7 +695,7 @@ export default function FormConvertSPIT() {
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-xl text-black">File Media</Label>
|
<Label className="text-xl">File Media</Label>
|
||||||
<div className="w-full ">
|
<div className="w-full ">
|
||||||
<Swiper
|
<Swiper
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
|
|
@ -709,7 +706,7 @@ export default function FormConvertSPIT() {
|
||||||
{detailThumb?.map((data: any) => (
|
{detailThumb?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide key={data.id}>
|
||||||
<img
|
<img
|
||||||
className="object-fill h-full w-full"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data}
|
src={data}
|
||||||
alt={` ${data.id}`}
|
alt={` ${data.id}`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -719,7 +716,7 @@ export default function FormConvertSPIT() {
|
||||||
<div className=" mt-2 ">
|
<div className=" mt-2 ">
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
onSwiper={setThumbsSwiper}
|
||||||
slidesPerView={6}
|
slidesPerView={8}
|
||||||
spaceBetween={8}
|
spaceBetween={8}
|
||||||
pagination={{
|
pagination={{
|
||||||
clickable: true,
|
clickable: true,
|
||||||
|
|
@ -730,7 +727,7 @@ export default function FormConvertSPIT() {
|
||||||
{detailThumb?.map((data: any) => (
|
{detailThumb?.map((data: any) => (
|
||||||
<SwiperSlide key={data.id}>
|
<SwiperSlide key={data.id}>
|
||||||
<img
|
<img
|
||||||
className="object-cover h-[60px] w-[80px]"
|
className="object-cover h-[60px] w-[80px] rounded-md"
|
||||||
src={data}
|
src={data}
|
||||||
alt={` ${data.id}`}
|
alt={` ${data.id}`}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,9 @@ export default function FormTeksDetail() {
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
||||||
|
|
||||||
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
|
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||||
|
|
||||||
let fileTypeId = "3";
|
let fileTypeId = "3";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -167,7 +170,7 @@ export default function FormTeksDetail() {
|
||||||
userLevelId == "216" &&
|
userLevelId == "216" &&
|
||||||
roleId == "3"
|
roleId == "3"
|
||||||
) {
|
) {
|
||||||
setIsMabesApprover(true);
|
setIsUserMabesApprover(true);
|
||||||
}
|
}
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
|
|
@ -210,6 +213,14 @@ export default function FormTeksDetail() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setupPlacementCheck = (length: number) => {
|
||||||
|
const temp = [];
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
temp.push([]);
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|
@ -255,9 +266,16 @@ export default function FormTeksDetail() {
|
||||||
}, [refresh, setValue]);
|
}, [refresh, setValue]);
|
||||||
|
|
||||||
const actionApproval = (e: string) => {
|
const actionApproval = (e: string) => {
|
||||||
|
const temp = [];
|
||||||
|
for (const element of detail.files) {
|
||||||
|
temp.push([]);
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
setStatus(e);
|
setStatus(e);
|
||||||
setModalOpen(true);
|
setFiles(detail.files);
|
||||||
|
|
||||||
setDescription("");
|
setDescription("");
|
||||||
|
setModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
|
|
@ -266,29 +284,42 @@ export default function FormTeksDetail() {
|
||||||
Number(status) == 2 ||
|
Number(status) == 2 ||
|
||||||
Number(status) == 4
|
Number(status) == 4
|
||||||
) {
|
) {
|
||||||
MySwal.fire({
|
save();
|
||||||
title: "Simpan Approval",
|
// MySwal.fire({
|
||||||
text: "",
|
// title: "Simpan Approval",
|
||||||
icon: "warning",
|
// text: "",
|
||||||
showCancelButton: true,
|
// icon: "warning",
|
||||||
cancelButtonColor: "#d33",
|
// showCancelButton: true,
|
||||||
confirmButtonColor: "#3085d6",
|
// cancelButtonColor: "#d33",
|
||||||
confirmButtonText: "Simpan",
|
// confirmButtonColor: "#3085d6",
|
||||||
}).then((result) => {
|
// confirmButtonText: "Simpan",
|
||||||
if (result.isConfirmed) {
|
// }).then((result) => {
|
||||||
save();
|
// if (result.isConfirmed) {
|
||||||
}
|
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPlacement = () => {
|
||||||
|
console.log("getPlaa", filePlacements);
|
||||||
|
const temp = [];
|
||||||
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
|
if (filePlacements[i].length !== 0) {
|
||||||
|
const now = filePlacements[i].filter((a) => a !== "all");
|
||||||
|
const data = { mediaFileId: files[i].id, placement: now.join(",") };
|
||||||
|
temp.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const data = {
|
const data = {
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
// files: isMabesApprover ? getPlacement() : [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -311,10 +342,44 @@ export default function FormTeksDetail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
submitApprovalSuccesss();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setupPlacement = (
|
||||||
|
index: number,
|
||||||
|
placement: string,
|
||||||
|
checked: boolean
|
||||||
|
) => {
|
||||||
|
let temp = [...filePlacements];
|
||||||
|
if (checked) {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
|
} else {
|
||||||
|
const now = temp[index];
|
||||||
|
now.push(placement);
|
||||||
|
if (now.length === 3 && !now.includes("all")) {
|
||||||
|
now.push("all");
|
||||||
|
}
|
||||||
|
temp[index] = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = [];
|
||||||
|
} else {
|
||||||
|
const now = temp[index].filter((a) => a !== placement);
|
||||||
|
console.log("now", now);
|
||||||
|
temp[index] = now;
|
||||||
|
if (now.length === 3 && now.includes("all")) {
|
||||||
|
const newData = now.filter((b) => b !== "all");
|
||||||
|
temp[index] = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
};
|
||||||
|
|
||||||
function handleDeleteFileApproval(id: number) {
|
function handleDeleteFileApproval(id: number) {
|
||||||
const selectedFiles = files.filter((file) => file.id != id);
|
const selectedFiles = files.filter((file) => file.id != id);
|
||||||
setFiles(selectedFiles);
|
setFiles(selectedFiles);
|
||||||
|
|
@ -473,7 +538,7 @@ export default function FormTeksDetail() {
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
onSwiper={setThumbsSwiper}
|
||||||
slidesPerView={6}
|
slidesPerView={8}
|
||||||
spaceBetween={8}
|
spaceBetween={8}
|
||||||
pagination={{ clickable: true }}
|
pagination={{ clickable: true }}
|
||||||
modules={[Pagination, Thumbs]}
|
modules={[Pagination, Thumbs]}
|
||||||
|
|
@ -644,7 +709,7 @@ export default function FormTeksDetail() {
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>Berikan Komentar</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file) => (
|
? files?.map((file, index) => (
|
||||||
<div
|
<div
|
||||||
key={file.id}
|
key={file.id}
|
||||||
className="flex flex-row gap-2 items-center"
|
className="flex flex-row gap-2 items-center"
|
||||||
|
|
@ -653,49 +718,92 @@ export default function FormTeksDetail() {
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
{file.fileName}
|
{file.fileName}
|
||||||
<a>
|
<a
|
||||||
|
onClick={() =>
|
||||||
|
handleDeleteFileApproval(file.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-2">
|
{isUserMabesApprover && (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex flex-row gap-2">
|
||||||
<Checkbox id="terms" />
|
<div className="flex items-center space-x-2">
|
||||||
<label
|
<Checkbox
|
||||||
htmlFor="terms"
|
id="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
value="all"
|
||||||
>
|
checked={filePlacements[index]?.includes(
|
||||||
Semua
|
"all"
|
||||||
</label>
|
)}
|
||||||
</div>
|
onCheckedChange={(e) =>
|
||||||
<div className="flex items-center space-x-2">
|
setupPlacement(index, "all", Boolean(e))
|
||||||
<Checkbox id="terms" />
|
}
|
||||||
<label
|
/>
|
||||||
htmlFor="terms"
|
<label
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
htmlFor="terms"
|
||||||
>
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
Nasional
|
>
|
||||||
</label>
|
Semua
|
||||||
</div>
|
</label>
|
||||||
<div className="flex items-center space-x-2">
|
</div>
|
||||||
<Checkbox id="terms" />
|
<div className="flex items-center space-x-2">
|
||||||
<label
|
<Checkbox
|
||||||
htmlFor="terms"
|
id="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
checked={filePlacements[index]?.includes(
|
||||||
>
|
"mabes"
|
||||||
Wilayah
|
)}
|
||||||
</label>
|
onCheckedChange={(e) =>
|
||||||
</div>
|
setupPlacement(index, "mabes", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Nasional
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
"polda"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
setupPlacement(index, "polda", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Wilayah
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox id="terms" />
|
<Checkbox
|
||||||
<label
|
id="terms"
|
||||||
htmlFor="terms"
|
checked={filePlacements[index]?.includes(
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
"international"
|
||||||
>
|
)}
|
||||||
Internasional
|
onCheckedChange={(e) =>
|
||||||
</label>
|
setupPlacement(
|
||||||
|
index,
|
||||||
|
"international",
|
||||||
|
Boolean(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Internasional
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
@ -778,14 +886,16 @@ export default function FormTeksDetail() {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={submitApprovalSuccesss}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={() => setModalOpen(false)}
|
onClick={() => {
|
||||||
|
setModalOpen(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -1329,6 +1329,7 @@ export default function FormTaskDetail() {
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<div className="">
|
<div className="">
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
color="primary"
|
color="primary"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
onClick={handleToggleInput}
|
onClick={handleToggleInput}
|
||||||
|
|
@ -1338,6 +1339,7 @@ export default function FormTaskDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
className="btn btn-primary ml-3 mr-3"
|
className="btn btn-primary ml-3 mr-3"
|
||||||
style={
|
style={
|
||||||
statusAcceptance ||
|
statusAcceptance ||
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Loading…
Reference in New Issue