pull main
This commit is contained in:
commit
397b30aa75
|
|
@ -47,6 +47,7 @@ import {
|
||||||
|
|
||||||
// Swiper
|
// Swiper
|
||||||
import { Swiper, SwiperSlide } from "swiper/react";
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
|
import { Swiper as SwiperType } from "swiper";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "swiper/css/free-mode";
|
import "swiper/css/free-mode";
|
||||||
import "swiper/css/navigation";
|
import "swiper/css/navigation";
|
||||||
|
|
@ -205,7 +206,7 @@ export default function FormConvertSPIT() {
|
||||||
|
|
||||||
// <-- changed: detailThumb is now FileType[] (objects from API) -->
|
// <-- changed: detailThumb is now FileType[] (objects from API) -->
|
||||||
const [detailThumb, setDetailThumb] = useState<FileType[]>([]);
|
const [detailThumb, setDetailThumb] = useState<FileType[]>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<SwiperType | null>(null);
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||||
|
|
@ -228,6 +229,13 @@ export default function FormConvertSPIT() {
|
||||||
checkUserPermissions();
|
checkUserPermissions();
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
|
// Handle Swiper re-initialization when detailThumb changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (thumbsSwiper && detailThumb.length > 0) {
|
||||||
|
thumbsSwiper.update();
|
||||||
|
}
|
||||||
|
}, [detailThumb, thumbsSwiper]);
|
||||||
|
|
||||||
const initializeComponent = async () => {
|
const initializeComponent = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
@ -281,7 +289,10 @@ export default function FormConvertSPIT() {
|
||||||
try {
|
try {
|
||||||
const response = await getTagsBySubCategoryId(categoryId);
|
const response = await getTagsBySubCategoryId(categoryId);
|
||||||
if (response?.data?.data?.length > 0) {
|
if (response?.data?.data?.length > 0) {
|
||||||
const tagsMerge = [...tags, response?.data?.data];
|
// Extract tag names from the response objects
|
||||||
|
const apiTags = response?.data?.data?.map((tag: any) => tag.tagName);
|
||||||
|
// Merge with existing tags, ensuring uniqueness
|
||||||
|
const tagsMerge = Array.from(new Set([...tags, ...apiTags]));
|
||||||
setTags(tagsMerge);
|
setTags(tagsMerge);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -331,16 +342,16 @@ export default function FormConvertSPIT() {
|
||||||
(details as any).contentRewriteDescription || ""
|
(details as any).contentRewriteDescription || ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Set category and load category tags
|
||||||
if (details.categoryId) {
|
if (details.categoryId) {
|
||||||
setSelectedCategoryId(details.categoryId);
|
setSelectedCategoryId(details.categoryId);
|
||||||
await loadTags(details.categoryId);
|
await loadTags(details.categoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set content tags and merge with category tags
|
||||||
if (details.contentTag) {
|
if (details.contentTag) {
|
||||||
const initialTags = details.contentTag
|
const contentTags = details.contentTag.split(",").map((tag: string) => tag.trim());
|
||||||
.split(",")
|
setTags(prev => Array.from(new Set([...prev, ...contentTags])));
|
||||||
.map((tag: string) => tag.trim());
|
|
||||||
setTags(initialTags);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load detail:", error);
|
console.error("Failed to load detail:", error);
|
||||||
|
|
@ -543,7 +554,6 @@ export default function FormConvertSPIT() {
|
||||||
|
|
||||||
const getPlacementData = () => {
|
const getPlacementData = () => {
|
||||||
const placementData: PlacementData[] = [];
|
const placementData: PlacementData[] = [];
|
||||||
console.log("filePlacements : ", filePlacements);
|
|
||||||
for (let i = 0; i < filePlacements.length; i++) {
|
for (let i = 0; i < filePlacements.length; i++) {
|
||||||
if (filePlacements[i].length > 0) {
|
if (filePlacements[i].length > 0) {
|
||||||
const placements = filePlacements[i];
|
const placements = filePlacements[i];
|
||||||
|
|
@ -949,68 +959,114 @@ export default function FormConvertSPIT() {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4 overflow-hidden">
|
||||||
<Swiper
|
<Swiper
|
||||||
|
key={`main-swiper-${detailThumb.length}`}
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
modules={[FreeMode, Navigation, Thumbs]}
|
modules={[FreeMode, Navigation, Thumbs]}
|
||||||
navigation={true}
|
navigation={true}
|
||||||
className="w-full h-96"
|
className="w-full h-96"
|
||||||
|
spaceBetween={10}
|
||||||
|
loop={false}
|
||||||
|
watchSlidesProgress={true}
|
||||||
|
allowTouchMove={true}
|
||||||
|
resistance={true}
|
||||||
|
resistanceRatio={0.85}
|
||||||
|
preventClicks={false}
|
||||||
|
preventClicksPropagation={false}
|
||||||
|
slidesPerView={1}
|
||||||
|
centeredSlides={true}
|
||||||
>
|
>
|
||||||
{detailThumb.map((item) => (
|
{detailThumb.map((item) => (
|
||||||
<SwiperSlide key={item.contentId}>
|
<SwiperSlide key={item.contentId} className="!w-full">
|
||||||
{item.contentType === "VIDEO" ? (
|
{item.contentType === "VIDEO" ? (
|
||||||
<div className="relative max-h-screen overflow-hidden">
|
<div className="relative w-full h-96 overflow-hidden rounded-lg">
|
||||||
<div className="w-full max-h-screen aspect-video">
|
<video
|
||||||
<div className="w-full h-full object-contain">
|
className="w-full h-full object-contain rounded-lg"
|
||||||
{/* main video player */}
|
src={item.contentFile}
|
||||||
<video
|
controls
|
||||||
className="object-contain h-full w-full rounded-lg"
|
playsInline
|
||||||
src={item.contentFile}
|
poster={item.thumbnailFileUrl || undefined}
|
||||||
controls
|
title={`Video ${item.contentId}`}
|
||||||
// playsInline to better on mobile
|
onError={(e) => {
|
||||||
playsInline
|
console.error('Video load error:', e);
|
||||||
// you can set poster if available: poster={item.thumbnailFileUrl}
|
e.currentTarget.style.display = 'none';
|
||||||
title={`Video ${item.contentId}`}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<div className="w-full h-96 flex items-center justify-center overflow-hidden rounded-lg">
|
||||||
src={item.contentFile}
|
<img
|
||||||
alt={`Media ${item.contentId}`}
|
src={item.contentFile}
|
||||||
className="w-full h-full object-cover rounded-lg"
|
alt={`Media ${item.contentId}`}
|
||||||
/>
|
className="max-w-full max-h-full object-contain rounded-lg"
|
||||||
|
style={{ maxWidth: '100%', maxHeight: '100%' }}
|
||||||
|
onError={(e) => {
|
||||||
|
console.error('Image load error:', e);
|
||||||
|
e.currentTarget.style.display = 'none';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
|
||||||
<Swiper
|
<Swiper
|
||||||
|
key={`thumbs-swiper-${detailThumb.length}`}
|
||||||
onSwiper={setThumbsSwiper}
|
onSwiper={setThumbsSwiper}
|
||||||
slidesPerView={8}
|
slidesPerView={8}
|
||||||
spaceBetween={8}
|
spaceBetween={8}
|
||||||
modules={[Pagination, Thumbs]}
|
freeMode={true}
|
||||||
|
watchSlidesProgress={true}
|
||||||
|
modules={[FreeMode, Thumbs]}
|
||||||
|
breakpoints={{
|
||||||
|
320: {
|
||||||
|
slidesPerView: 3,
|
||||||
|
spaceBetween: 4,
|
||||||
|
},
|
||||||
|
640: {
|
||||||
|
slidesPerView: 5,
|
||||||
|
spaceBetween: 6,
|
||||||
|
},
|
||||||
|
768: {
|
||||||
|
slidesPerView: 6,
|
||||||
|
spaceBetween: 8,
|
||||||
|
},
|
||||||
|
1024: {
|
||||||
|
slidesPerView: 8,
|
||||||
|
spaceBetween: 8,
|
||||||
|
},
|
||||||
|
}}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
loop={false}
|
||||||
|
allowTouchMove={true}
|
||||||
|
resistance={true}
|
||||||
|
resistanceRatio={0.85}
|
||||||
|
preventClicks={false}
|
||||||
|
preventClicksPropagation={false}
|
||||||
>
|
>
|
||||||
{detailThumb.map((item) => (
|
{detailThumb.map((item) => (
|
||||||
<SwiperSlide key={`thumb-${item.contentId}`}>
|
<SwiperSlide key={`thumb-${item.contentId}`} className="!w-auto flex-shrink-0">
|
||||||
{item.contentType === "VIDEO" ? (
|
{item.contentType === "VIDEO" ? (
|
||||||
<div className="relative w-full h-16 rounded cursor-pointer overflow-hidden">
|
<div className="relative w-20 h-16 rounded cursor-pointer overflow-hidden flex-shrink-0">
|
||||||
{/* use preload metadata so browser doesn't download full video */}
|
{/* use preload metadata so browser doesn't download full video */}
|
||||||
<video
|
<video
|
||||||
src={item.contentFile}
|
src={item.contentFile}
|
||||||
className="w-full h-16 object-cover"
|
className="w-full h-full object-cover"
|
||||||
muted
|
muted
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
playsInline
|
playsInline
|
||||||
// no controls in thumbnail
|
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
onError={(e) => {
|
||||||
|
console.error('Thumbnail video load error:', e);
|
||||||
|
e.currentTarget.style.display = 'none';
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-black/30 pointer-events-none">
|
<div className="absolute inset-0 flex items-center justify-center bg-black/30 pointer-events-none">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="w-6 h-6 text-white"
|
className="w-4 h-4 text-white"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
|
|
@ -1019,11 +1075,17 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<div className="w-20 h-16 rounded cursor-pointer overflow-hidden flex-shrink-0">
|
||||||
src={item.contentFile}
|
<img
|
||||||
alt={`Thumbnail ${item.contentId}`}
|
src={item.contentFile}
|
||||||
className="w-full h-16 object-cover rounded cursor-pointer"
|
alt={`Thumbnail ${item.contentId}`}
|
||||||
/>
|
className="w-full h-full object-cover"
|
||||||
|
onError={(e) => {
|
||||||
|
console.error('Thumbnail image load error:', e);
|
||||||
|
e.currentTarget.style.display = 'none';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue