This commit is contained in:
Sabda Yagra 2025-07-19 19:51:23 +07:00
parent 978c8b364f
commit 968f2642cc
4 changed files with 83 additions and 146 deletions

View File

@ -2,34 +2,19 @@ import React, { useRef, useState, useEffect } from "react";
interface AudioPlayerProps {
urlAudio: string;
fileName: string; // ✅ Tambahkan props ini
fileName: string;
}
const AudioPlayer: React.FC<AudioPlayerProps> = ({ urlAudio, fileName }) => {
const audioRef = useRef<HTMLAudioElement>(null);
const [currentTime, setCurrentTime] = useState(0);
const playAudio = () => {
audioRef.current?.play();
};
const pauseAudio = () => {
audioRef.current?.pause();
};
const stopAudio = () => {
if (audioRef.current) {
audioRef.current.pause();
audioRef.current.currentTime = 0;
}
};
useEffect(() => {
const audio = audioRef.current;
if (!audio) return;
const updateTime = () => {
setCurrentTime(audio.currentTime);
setCurrentTime(audio?.currentTime);
};
audio.addEventListener("timeupdate", updateTime);
@ -38,22 +23,17 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ urlAudio, fileName }) => {
};
}, []);
const formatTime = (time: number) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60).toString().padStart(2, "0");
return `${minutes}:${seconds}`;
};
return (
<div className="mt-2">
<h2 className="text-lg font-semibold">{fileName}</h2>
{/* <a href={urlAudio} target="_blank">{urlAudio}</a> */}
<audio ref={audioRef} src={urlAudio} controls className="mt-1 w-full" />
<div className="mt-2 space-x-2">
{/* <div className="mt-2 space-x-2">
<button onClick={playAudio}> Play</button>
<button onClick={pauseAudio}> Pause</button>
<button onClick={stopAudio}> Stop</button>
</div>
<div className="mt-1 text-sm text-gray-500">{formatTime(currentTime)}</div>
<div className="mt-1 text-sm text-gray-500">{formatTime(currentTime)}</div> */}
</div>
);
};

View File

@ -83,7 +83,7 @@ type Category = {
type FileType = {
id: number;
url: string;
secondaryUrl: string;
thumbnailFileUrl: string;
fileName: string;
};
@ -121,13 +121,11 @@ export default function FormAudioDetail() {
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const roleId = getCookiesDecrypt("urie");
const [modalOpen, setModalOpen] = useState(false);
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
@ -249,7 +247,7 @@ export default function FormAudioDetail() {
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
setSelectedCategory(findCategory.id);
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
@ -274,6 +272,8 @@ export default function FormAudioDetail() {
const details = response?.data?.data;
console.log("detail", details);
setFiles(details?.files);
console.log("ISI FILES:", details?.files);
setDetail(details);
setMain({
type: details?.fileType.name,
@ -293,19 +293,23 @@ export default function FormAudioDetail() {
setSelectedTarget(String(details.category.id));
const filesData = details?.files || [];
const audioFiles = filesData.filter(
(file: any) =>
file.contentType &&
(file.contentType.startsWith("audio/") ||
file.contentType.includes("mpeg"))
);
// const audioFiles = filesData.filter(
// (file: any) =>
// file.contentType &&
// (file.contentType.startsWith("audio/") ||
// file.contentType.includes("mpeg"))
// );
// const audioFiles = filesData.filter(
// (file: any) =>
// file.contentType && /^audio\/(mpeg|mp3|wav)$/.test(file.contentType)
// );
const fileUrls = audioFiles.map((file: { url: string }) =>
file.url ? file.url : ""
);
console.log("Audio file URLs:", fileUrls);
// const fileUrls = audioFiles.map((file: { secondaryUrl: string }) =>
// file.secondaryUrl ? file.secondaryUrl : ""
// );
// console.log("Audio file URLs:", fileUrls);
setDetailThumb(fileUrls);
// setDetailThumb(fileUrls);
const approvals = await getDataApprovalByMediaUpload(details?.id);
setApproval(approvals?.data?.data);
@ -570,17 +574,19 @@ export default function FormAudioDetail() {
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="w-full">
{detailThumb.map((url, index) => (
<div key={index}>
{files.map((file, index) => (
<AudioPlayer
key={index}
urlAudio={file.url}
fileName={file.fileName}
/>
))}
</div>
))}
{files.length === 0 ? (
<p className="text-center text-gray-500">
Tidak ada file media
</p>
) : (
files.map((file, index) => (
<AudioPlayer
key={index}
urlAudio={file?.secondaryUrl}
fileName={file?.fileName}
/>
))
)}
</div>
</div>
</div>

View File

@ -211,9 +211,6 @@ export default function FormAudio() {
tags: z
.array(z.string().min(1))
.min(1, { message: "Wajib isi minimal 1 tag" }),
publishedFor: z
.array(z.string())
.min(1, { message: "Minimal 1 target publish harus dipilih." }),
});
const {
@ -230,7 +227,6 @@ export default function FormAudio() {
rewriteDescription: "",
category: "",
tags: [],
publishedFor: [],
},
});
@ -483,8 +479,10 @@ export default function FormAudio() {
const handleCheckboxChange = (id: string): void => {
if (id === "all") {
if (publishedFor.includes("all")) {
// Uncheck all checkboxes
setPublishedFor([]);
} else {
// Select all checkboxes
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
@ -495,6 +493,8 @@ export default function FormAudio() {
const updatedPublishedFor = publishedFor.includes(id)
? publishedFor.filter((item) => item !== id)
: [...publishedFor, id];
// Remove "all" if any checkbox is unchecked
if (publishedFor.includes("all") && id !== "all") {
setPublishedFor(updatedPublishedFor.filter((item) => item !== "all"));
} else {
@ -505,6 +505,7 @@ export default function FormAudio() {
useEffect(() => {
if (articleBody) {
// Set ke dua field jika rewrite juga aktif
setValue("description", articleBody);
setValue("rewriteDescription", articleBody);
}
@ -512,21 +513,13 @@ export default function FormAudio() {
const save = async (data: AudioSchema) => {
loading();
if (files.length === 0) {
MySwal.fire("Error", "Minimal 1 file harus diunggah.", "error");
return;
}
const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title;
// const finalDescription = articleBody || data.description;
const finalDescription = isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return;
@ -574,36 +567,40 @@ export default function FormAudio() {
const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData);
if (response?.error) {
MySwal.fire("Error", response?.message, "error");
return;
}
Cookies.set("idCreate", response?.data?.data, { expires: 1 });
id = response?.data?.data;
const formMedia = new FormData();
const thumbnail = files[0];
formMedia.append("file", thumbnail);
console.log("Thumbnail : ", files[0]);
formMedia.append("file", files[0]);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) {
error(responseThumbnail?.message);
return false;
}
}
const progressInfoArr = files.map((item) => ({
percentage: 0,
fileName: item.name,
}));
const progressInfoArr = [];
for (const item of files) {
progressInfoArr.push({ percentage: 0, fileName: item.name });
}
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
await uploadResumableFile(index, String(id), item, "0");
});
Cookies.remove("idCreate");
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
};
const onSubmit = (data: AudioSchema) => {
@ -650,7 +647,7 @@ export default function FormAudio() {
filename: file.name,
filetype: file.type,
duration,
isWatermark: "false",
isWatermark: "false",
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
@ -1416,73 +1413,29 @@ export default function FormAudio() {
)}
</div>
<Controller
control={control}
name="publishedFor"
render={({ field }) => (
<div className="px-3 py-3">
<div className="flex flex-col gap-3 space-y-2">
<Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
{options.map((option) => {
const isAllChecked =
field.value.length ===
options.filter((opt: any) => opt.id !== "all").length;
const isChecked =
<div className="px-3 py-3">
<div className="flex flex-col gap-3 space-y-2">
<Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
{options.map((option) => (
<div key={option.id} className="flex gap-2 items-center">
<Checkbox
id={option.id}
checked={
option.id === "all"
? isAllChecked
: field.value.includes(option.id);
const handleChange = () => {
let updated: string[] = [];
if (option.id === "all") {
updated = isAllChecked
? []
: options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id);
} else {
updated = isChecked
? field.value.filter((val) => val !== option.id)
: [...field.value, option.id];
if (isAllChecked && option.id !== "all") {
updated = updated.filter((val) => val !== "all");
}
}
field.onChange(updated);
setPublishedFor(updated);
};
return (
<div
key={option.id}
className="flex gap-2 items-center"
>
<Checkbox
id={option.id}
checked={isChecked}
onCheckedChange={handleChange}
/>
<Label htmlFor={option.id}>{option.label}</Label>
</div>
);
})}
{errors.publishedFor && (
<p className="text-red-500 text-sm">
{errors.publishedFor.message}
</p>
)}
? publishedFor.length ===
options.filter((opt: any) => opt.id !== "all")
.length
: publishedFor.includes(option.id)
}
onCheckedChange={() => handleCheckboxChange(option.id)}
/>
<Label htmlFor={option.id}>{option.label}</Label>
</div>
</div>
)}
/>
))}
</div>
</div>
</Card>
<div className="flex flex-row justify-end gap-3">
<div className="mt-4">

View File

@ -99,9 +99,7 @@ const DetailAudio = () => {
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailAudio", response);
const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]);
setIsFromSPIT(response?.data?.data?.isFromSPIT);
setWidth(window.innerWidth);
setContent(response?.data?.data);
@ -405,7 +403,7 @@ const DetailAudio = () => {
const { default: WaveSurfer } = await import("wavesurfer.js");
if (wavesurfer.current) {
wavesurfer.current.destroy(); // 🔥 Hapus instance lama sebelum membuat yang baru
wavesurfer.current.destroy();
}
setPlaying(false);
@ -414,7 +412,7 @@ const DetailAudio = () => {
return [
Math.floor((time % 3600) / 60),
// minutes
`00${Math.floor(time % 60)}`.slice(-2), // seconds
`00${Math.floor(time % 60)}`.slice(-2),
].join(":");
};
@ -445,7 +443,7 @@ const DetailAudio = () => {
return () => {
if (wavesurfer.current) {
wavesurfer.current.destroy(); // 🔥 Hapus saat unmount
wavesurfer.current.destroy();
}
};
}