fixing
This commit is contained in:
parent
978c8b364f
commit
968f2642cc
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue