Merge branch 'main' of https://gitlab.com/hanifsalafi/mediahub_redesign into prod
This commit is contained in:
commit
0ec1ca9297
|
|
@ -151,8 +151,10 @@ const EventModal = ({
|
|||
satker: false,
|
||||
international: false,
|
||||
});
|
||||
|
||||
const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType
|
||||
const levelNumber = getCookiesDecrypt("ulne");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const poldaState = Cookies.get("state");
|
||||
const [agendaType, setAgendaType] = React.useState("");
|
||||
const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
|
||||
const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
|
||||
const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
|
||||
|
|
@ -253,7 +255,7 @@ const EventModal = ({
|
|||
|
||||
useEffect(() => {
|
||||
setIsDatePickerOpen(false);
|
||||
}, [onClose])
|
||||
}, [onClose]);
|
||||
|
||||
const handleCheckboxChange = (levelId: number) => {
|
||||
setCheckedLevels((prev) => {
|
||||
|
|
@ -393,13 +395,7 @@ const EventModal = ({
|
|||
setIsAudioUploadFinish(true);
|
||||
}
|
||||
audioFiles.map(async (item: FileWithPreview, index: number) => {
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
"4",
|
||||
"0"
|
||||
);
|
||||
await uploadResumableFile(index, String(id), item, "4", "0");
|
||||
});
|
||||
if (publish) {
|
||||
setIsPublishing(true);
|
||||
|
|
@ -431,9 +427,6 @@ const EventModal = ({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
// console.log("Event data:", event);
|
||||
// console.log("Selected date:", selectedDate);
|
||||
|
||||
if (selectedDate) {
|
||||
setDate({
|
||||
from: selectedDate.date,
|
||||
|
|
@ -742,7 +735,10 @@ const EventModal = ({
|
|||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="date">Tanggal</Label>
|
||||
<Popover open={isDatePickerOpen} onOpenChange={() => setIsDatePickerOpen(true)}>
|
||||
<Popover
|
||||
open={isDatePickerOpen}
|
||||
onOpenChange={() => setIsDatePickerOpen(true)}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -789,26 +785,31 @@ const EventModal = ({
|
|||
<div className="space-y-1.5">
|
||||
<Label htmlFor="wilayahPublish">Jenis Agenda</Label>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div>
|
||||
<Checkbox
|
||||
id="semua"
|
||||
checked={wilayahPublish.semua}
|
||||
onCheckedChange={() => toggleWilayah("semua")}
|
||||
/>
|
||||
<label htmlFor="semua" className="ml-2 text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="nasional"
|
||||
checked={wilayahPublish.nasional}
|
||||
onCheckedChange={() => toggleWilayah("nasional")}
|
||||
/>
|
||||
<label htmlFor="nasional" className="ml-2 text-sm mr-2">
|
||||
Nasional
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Checkbox
|
||||
id="semua"
|
||||
checked={wilayahPublish.semua}
|
||||
onCheckedChange={() => toggleWilayah("semua")}
|
||||
/>
|
||||
<label htmlFor="semua" className="ml-2 text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{roleId === 1 && (
|
||||
<div>
|
||||
<Checkbox
|
||||
id="nasional"
|
||||
checked={wilayahPublish.nasional}
|
||||
onCheckedChange={() => toggleWilayah("nasional")}
|
||||
/>
|
||||
<label htmlFor="nasional" className="ml-2 text-sm mr-2">
|
||||
Nasional
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Checkbox
|
||||
id="polda"
|
||||
|
|
@ -829,59 +830,65 @@ const EventModal = ({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="polres"
|
||||
checked={wilayahPublish.polres}
|
||||
onCheckedChange={() => toggleWilayah("polres")}
|
||||
/>
|
||||
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
||||
Polres
|
||||
</label>
|
||||
{wilayahPublish.polres && (
|
||||
<UnitMapping
|
||||
unit="Polres"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedPolres}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolres(data)
|
||||
}
|
||||
{(roleId === 1 || roleId === 4 || roleId === 3) && (
|
||||
<div>
|
||||
<Checkbox
|
||||
id="polres"
|
||||
checked={wilayahPublish.polres}
|
||||
onCheckedChange={() => toggleWilayah("polres")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="satker"
|
||||
checked={wilayahPublish.satker}
|
||||
onCheckedChange={() => toggleWilayah("satker")}
|
||||
/>
|
||||
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||
Satker
|
||||
</label>
|
||||
{wilayahPublish.satker && (
|
||||
<UnitMapping
|
||||
unit="Satker"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedSatker}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedSatker(data)
|
||||
}
|
||||
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
||||
Polres
|
||||
</label>
|
||||
{wilayahPublish.polres && (
|
||||
<UnitMapping
|
||||
unit="Polres"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedPolres}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolres(data)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{(roleId === 1 || roleId === 2) && (
|
||||
<div>
|
||||
<Checkbox
|
||||
id="satker"
|
||||
checked={wilayahPublish.satker}
|
||||
onCheckedChange={() => toggleWilayah("satker")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="international"
|
||||
checked={wilayahPublish.international}
|
||||
onCheckedChange={() => toggleWilayah("international")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="international"
|
||||
className="ml-2 text-sm mr-2"
|
||||
>
|
||||
Internasional
|
||||
</label>
|
||||
</div>
|
||||
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||
Satker
|
||||
</label>
|
||||
{wilayahPublish.satker && (
|
||||
<UnitMapping
|
||||
unit="Satker"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedSatker}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedSatker(data)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{roleId === 1 && (
|
||||
<div>
|
||||
<Checkbox
|
||||
id="international"
|
||||
checked={wilayahPublish.international}
|
||||
onCheckedChange={() => toggleWilayah("international")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="international"
|
||||
className="ml-2 text-sm mr-2"
|
||||
>
|
||||
Internasional
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,18 @@ export default function DetailContentBlast() {
|
|||
close();
|
||||
|
||||
const detailData = res?.data?.data;
|
||||
let updatedUrl = detailData.contentUrl;
|
||||
|
||||
const domainsToUpdate = ["mediahub.polri.go.id", "netidhub.com"];
|
||||
|
||||
domainsToUpdate.forEach((domain) => {
|
||||
if (
|
||||
updatedUrl.includes(domain) &&
|
||||
!updatedUrl.includes(`${domain}/in`)
|
||||
) {
|
||||
updatedUrl = updatedUrl.replace(domain, `${domain}/in`);
|
||||
}
|
||||
});
|
||||
if (detailData && detailData.id === Number(id)) {
|
||||
setDetail({
|
||||
id: detailData.id,
|
||||
|
|
@ -41,7 +52,7 @@ export default function DetailContentBlast() {
|
|||
subject: detailData.subject,
|
||||
sendTime: detailData.sendTime,
|
||||
thumbnail: detailData.thumbnail,
|
||||
contentUrl: detailData.contentUrl,
|
||||
contentUrl: updatedUrl,
|
||||
});
|
||||
} else {
|
||||
setNotFound(true);
|
||||
|
|
|
|||
|
|
@ -144,17 +144,30 @@ export default function ContentBlast(props: { type: string }) {
|
|||
const response = await detailMediaSummary(String(id));
|
||||
const details = response?.data?.data;
|
||||
let pageUrl = details?.pageUrl || "";
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
pageUrl = pageUrl.replace(/(\.com|\.id)(\/|$)/, "$1/in$2");
|
||||
if (pageUrl.includes("mediahub.polri.go.id")) {
|
||||
pageUrl = pageUrl.replace(
|
||||
/(\.id)(\/|$)/,
|
||||
(match: any, p1: any, p2: any) => {
|
||||
return p2.startsWith("/in") ? match : `${p1}/in${p2}`;
|
||||
}
|
||||
);
|
||||
}
|
||||
if (details != undefined) {
|
||||
form.setValue("thumbnail", details.smallThumbnailLink);
|
||||
let body = `<div><p>Berita hari ini !!!</p><div style='margin-top:20px;border:1px solid;border-radius:10px;width:500px;background-color:#f7f7f7'><div><img style='width:500px;height:auto;border-radius:10px;object-fit:cover' src='${
|
||||
details?.smallThumbnailLink
|
||||
}'></div> <a style='padding:5px 20px;margin:0;text-decoration:none' href='${pageUrl}'>${pageUrl}</a><h3 style='padding:5px 20px;margin:0'>${
|
||||
details?.title
|
||||
}</h3><p style='padding:0 20px;margin:0;margin-bottom:10px'>
|
||||
${textEllipsis(details?.description, 150)}</p></div></div>`;
|
||||
let body = `<div><p>Berita hari ini !!!</p>
|
||||
<div style='margin-top:20px;border:1px solid;border-radius:10px;width:500px;background-color:#f7f7f7'>
|
||||
<div>
|
||||
<img style='width:500px;height:auto;border-radius:10px;object-fit:cover' src='${
|
||||
details?.smallThumbnailLink
|
||||
}'>
|
||||
</div>
|
||||
<a style='padding:5px 20px;margin:0;text-decoration:none' href='${pageUrl}'>${pageUrl}</a>
|
||||
<h3 style='padding:5px 20px;margin:0'>${details?.title}</h3>
|
||||
<p style='padding:0 20px;margin:0;margin-bottom:10px'>
|
||||
${textEllipsis(details?.description, 150)}
|
||||
</p>
|
||||
</div>
|
||||
</div>`;
|
||||
form.setValue("title", `${details?.title}`);
|
||||
form.setValue(
|
||||
"url",
|
||||
|
|
@ -208,6 +221,7 @@ export default function ContentBlast(props: { type: string }) {
|
|||
<FormItem>
|
||||
<FormLabel>Subject</FormLabel>
|
||||
<Select
|
||||
className="z-50"
|
||||
options={dataSelectCampaign}
|
||||
closeMenuOnSelect={false}
|
||||
components={animatedComponent}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,17 @@ type Category = {
|
|||
name: string;
|
||||
};
|
||||
|
||||
type PlacementType = "all" | "mabes" | "polda" | "international" | string;
|
||||
|
||||
interface FilePlacement {
|
||||
mediaFileId: number;
|
||||
placements?: PlacementType[];
|
||||
}
|
||||
|
||||
interface TempFileItem {
|
||||
id: number | string;
|
||||
}
|
||||
|
||||
type Detail = {
|
||||
id: string;
|
||||
title: string;
|
||||
|
|
@ -257,6 +268,7 @@ export default function FormImageUpdate() {
|
|||
|
||||
// Set the selected target to the category ID from details
|
||||
setSelectedTarget(String(details.category.id));
|
||||
setTempFile(details?.files);
|
||||
|
||||
setValue("title", details.title);
|
||||
setValue("description", details.htmlDescription);
|
||||
|
|
@ -643,6 +655,77 @@ export default function FormImageUpdate() {
|
|||
</Button>
|
||||
</div>
|
||||
));
|
||||
type PlacementType = "all" | "mabes" | "polda" | "international" | string;
|
||||
|
||||
interface FilePlacement {
|
||||
mediaFileId: number;
|
||||
placements?: PlacementType[];
|
||||
}
|
||||
|
||||
interface TempFileItem {
|
||||
id: number | string;
|
||||
// tambahkan properti lain kalau ada
|
||||
}
|
||||
|
||||
const [tempFile, setTempFile] = useState<TempFileItem[]>([]);
|
||||
const [filePlacements, setFilePlacements] = useState<FilePlacement[]>([]);
|
||||
|
||||
|
||||
const setupPlacement = (id: number | string, placement: PlacementType) => {
|
||||
console.log(`FileDestination.leng:: ${id}_${placement}`);
|
||||
const arrayFile: FilePlacement[] = [];
|
||||
|
||||
for (let i = 0; i < tempFile?.length; i++) {
|
||||
const element = tempFile[i];
|
||||
|
||||
if (element.id == id) {
|
||||
const findPlacementIdx = filePlacements.findIndex(
|
||||
(o) => Number(o.mediaFileId) === Number(id)
|
||||
);
|
||||
|
||||
if (findPlacementIdx > -1) {
|
||||
const findPlacement = filePlacements[findPlacementIdx];
|
||||
|
||||
if (findPlacement?.placements?.includes(placement)) {
|
||||
if (placement === "all") {
|
||||
findPlacement.placements = undefined;
|
||||
} else {
|
||||
findPlacement.placements = findPlacement.placements.filter(
|
||||
(val) => val !== placement
|
||||
);
|
||||
if (findPlacement.placements?.includes("all")) {
|
||||
findPlacement.placements = findPlacement.placements.filter(
|
||||
(val) => val !== "all"
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (placement === "all") {
|
||||
findPlacement.placements = [
|
||||
"all",
|
||||
"mabes",
|
||||
"polda",
|
||||
"international",
|
||||
];
|
||||
} else if (findPlacement.placements) {
|
||||
findPlacement.placements = [...findPlacement.placements, placement];
|
||||
} else {
|
||||
findPlacement.placements = [placement];
|
||||
}
|
||||
} else {
|
||||
const file: FilePlacement = {
|
||||
mediaFileId: Number(element.id),
|
||||
placements: [placement],
|
||||
};
|
||||
|
||||
arrayFile.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const finalPlacements = [...filePlacements, ...arrayFile];
|
||||
setFilePlacements(finalPlacements);
|
||||
console.log("FileDestination.leng::", finalPlacements);
|
||||
};
|
||||
|
||||
const handleCheckboxChangeImage = (fileId: number, value: string) => {
|
||||
setSelectedOptions((prev: any) => {
|
||||
|
|
@ -896,11 +979,8 @@ export default function FormImageUpdate() {
|
|||
checked={selectedOptions[
|
||||
file.id
|
||||
]?.includes("all")}
|
||||
onChange={() =>
|
||||
handleCheckboxChangeImage(
|
||||
file.id,
|
||||
"all"
|
||||
)
|
||||
onChange={(e) =>
|
||||
setupPlacement(file.id, e.target.value)
|
||||
}
|
||||
className="form-checkbox"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
import { getCategoryData, getPublicCategoryData } from "@/service/landing/landing";
|
||||
import {
|
||||
getCategoryData,
|
||||
getPublicCategoryData,
|
||||
} from "@/service/landing/landing";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Reveal } from "./Reveal";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "../ui/carousel";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "../ui/carousel";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Button } from "../ui/button";
|
||||
import ImageBlurry from "../ui/image-blurry";
|
||||
|
||||
const ContentCategory = (props: { group?: string; type: string }) => {
|
||||
const [categories, setCategories] = useState<any>();
|
||||
|
|
@ -19,14 +29,26 @@ const ContentCategory = (props: { group?: string; type: string }) => {
|
|||
const satkerName = params?.satker_name;
|
||||
const router = useRouter();
|
||||
|
||||
let prefixPath = poldaName ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` : "/";
|
||||
let prefixPath = poldaName
|
||||
? `/polda/${poldaName}`
|
||||
: satkerName
|
||||
? `/satker/${satkerName}`
|
||||
: "/";
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, []);
|
||||
const initFetch = async () => {
|
||||
const response = await getPublicCategoryData(
|
||||
props.group == "mabes" ? "" : props.group == "polda" && poldaName && String(poldaName)?.length > 1 ? poldaName : props.group == "satker" && satkerName && String(satkerName)?.length > 1 ? "satker-" + satkerName : "",
|
||||
props.group == "mabes"
|
||||
? ""
|
||||
: props.group == "polda" && poldaName && String(poldaName)?.length > 1
|
||||
? poldaName
|
||||
: props.group == "satker" &&
|
||||
satkerName &&
|
||||
String(satkerName)?.length > 1
|
||||
? "satker-" + satkerName
|
||||
: "",
|
||||
"",
|
||||
locale == "en" ? true : false
|
||||
);
|
||||
|
|
@ -52,7 +74,10 @@ const ContentCategory = (props: { group?: string; type: string }) => {
|
|||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||
</svg>`;
|
||||
|
||||
const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
|
||||
const toBase64 = (str: string) =>
|
||||
typeof window === "undefined"
|
||||
? Buffer.from(str).toString("base64")
|
||||
: window.btoa(str);
|
||||
|
||||
return (
|
||||
<div className="px-4 lg:px-0 py-10">
|
||||
|
|
@ -61,22 +86,34 @@ const ContentCategory = (props: { group?: string; type: string }) => {
|
|||
<h2 className="text-start text-lg md:text-xl font-bold text-[#bb3523] border-b-2 border-[#bb3523] mb-4 uppercase">
|
||||
{pathname?.split("/")[1] == "in" ? (
|
||||
<>
|
||||
<span className="text-[#bb3523] dark:text-white">{t("category", { defaultValue: "Category" })} </span>
|
||||
<span className="text-[#bb3523] dark:text-white">
|
||||
{t("category", { defaultValue: "Category" })}
|
||||
</span>
|
||||
{t("content", { defaultValue: "Content" })}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-[#bb3523] dark:text-white">{t("content", { defaultValue: "Content" })} </span>
|
||||
<span className="text-[#bb3523] dark:text-white">
|
||||
{t("content", { defaultValue: "Content" })}
|
||||
</span>
|
||||
{t("category", { defaultValue: "Category" })}
|
||||
</>
|
||||
)}
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{(seeAllValue ? categories : categories?.slice(0, 4))?.map((category: any) => (
|
||||
<div key={category?.id}>
|
||||
<div onClick={() => router.push(`${prefixPath}all/filter?category=${category?.id}`)} className="cursor-pointer relative group rounded-md overflow-hidden shadow-md hover:shadow-lg block">
|
||||
{/* Gambar */}
|
||||
<Image
|
||||
{(seeAllValue ? categories : categories?.slice(0, 4))?.map(
|
||||
(category: any) => (
|
||||
<div key={category?.id}>
|
||||
<div
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`${prefixPath}all/filter?category=${category?.id}`
|
||||
)
|
||||
}
|
||||
className="cursor-pointer relative group rounded-md overflow-hidden shadow-md hover:shadow-lg block"
|
||||
>
|
||||
{/* Gambar */}
|
||||
{/* <Image
|
||||
priority={true}
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`}
|
||||
alt="category"
|
||||
|
|
@ -84,25 +121,42 @@ const ContentCategory = (props: { group?: string; type: string }) => {
|
|||
height={1440}
|
||||
src={category?.smallThumbnailLink}
|
||||
className="w-full lg:h-[300px] h-40 object-cover group-hover:scale-110 transition-transform duration-300"
|
||||
/>
|
||||
/> */}
|
||||
<ImageBlurry
|
||||
src={category?.smallThumbnailLink}
|
||||
alt="gambar-utama"
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
)}`}
|
||||
priority={true}
|
||||
className="w-full lg:h-[300px] h-40 object-contain group-hover:scale-110 transition-transform duration-300"
|
||||
/>
|
||||
|
||||
{/* Overlay gelap */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-50 group-hover:bg-opacity-35 transition-all duration-300 rounded-md"></div>
|
||||
{/* Overlay gelap */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-50 group-hover:bg-opacity-35 transition-all duration-300 rounded-md"></div>
|
||||
|
||||
{/* Judul */}
|
||||
<div className="absolute bottom-5 left-1/2 transform -translate-x-1/2 text-white">
|
||||
<h3 className="text-sm font-semibold text-center">{category?.name}</h3>
|
||||
{/* Judul */}
|
||||
<div className="absolute bottom-5 left-1/2 transform -translate-x-1/2 text-white">
|
||||
<h3 className="text-sm font-semibold text-center">
|
||||
{category?.name}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tombol See More / See Less */}
|
||||
{categories?.length > 4 && (
|
||||
<div className="flex items-center flex-row justify-center mt-6">
|
||||
<Button onClick={() => setSeeAllValue(!seeAllValue)} className="bg-white dark:bg-black hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]">
|
||||
{seeAllValue ? t("seeLess", { defaultValue: "See Less" }) : t("seeMore", { defaultValue: "See More" })}
|
||||
<Button
|
||||
onClick={() => setSeeAllValue(!seeAllValue)}
|
||||
className="bg-white dark:bg-black hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]"
|
||||
>
|
||||
{seeAllValue
|
||||
? t("seeLess", { defaultValue: "See Less" })
|
||||
: t("seeMore", { defaultValue: "See More" })}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
|
|||
import { Link } from "@/i18n/routing";
|
||||
import { listBannerHero } from "@/service/settings/settings";
|
||||
import ImageBlurry from "../ui/image-blurry";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
|
||||
type HeroModalProps = {
|
||||
onClose: () => void;
|
||||
|
|
@ -161,7 +162,7 @@ const HeroModal = ({
|
|||
pagination={{ dynamicBullets: true }}
|
||||
modules={[Pagination, Autoplay]}
|
||||
onSwiper={(swiper) => (swiperRef.current = swiper)}
|
||||
autoplay={{ delay: 3000 }}
|
||||
autoplay={{ delay: 10000 }}
|
||||
className="mySwiper w-full"
|
||||
>
|
||||
{dataContent?.map((list: any) => (
|
||||
|
|
@ -173,7 +174,7 @@ const HeroModal = ({
|
|||
>
|
||||
✕
|
||||
</button>
|
||||
{/* <Image
|
||||
<Image
|
||||
priority={true}
|
||||
src={list?.smallThumbnailLink}
|
||||
alt="gambar-utama"
|
||||
|
|
@ -182,17 +183,14 @@ const HeroModal = ({
|
|||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
)}`}
|
||||
className="w-full h-[310px] lg:h-[420px] rounded-lg object-cover"
|
||||
/> */}
|
||||
<ImageBlurry
|
||||
className="w-full h-[310px] lg:h-[420px] rounded-lg object-contain bg-black"
|
||||
/>
|
||||
{/* <ImageBlurry
|
||||
priority={true}
|
||||
src={list?.smallThumbnailLink}
|
||||
alt="gambar-utama"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
className="w-full h-[310px] lg:h-[420px] rounded-lg object-contain"
|
||||
/> */}
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black/30 backdrop-brightness-50 text-white pb-4 px-4 pt-8 rounded-bl-2xl rounded-tr-2xl mx-3 mb-2">
|
||||
<div className="absolute top-0 left-0 bottom-0 w-2 bg-[#bb3523] rounded-bl-lg"></div>
|
||||
<span className="absolute top-0 left-0 mt-2 mb-3 mx-3 bg-[#bb3523] text-white text-xs font-semibold uppercase px-2 py-1 rounded">
|
||||
|
|
@ -456,7 +454,7 @@ const HeroNew = (props: { group?: string }) => {
|
|||
|
||||
const interval = setInterval(() => {
|
||||
setScrollIndex((prevIndex) => (prevIndex + 1) % content.length);
|
||||
}, 3000);
|
||||
}, 10000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [content]);
|
||||
|
|
@ -577,24 +575,29 @@ const HeroNew = (props: { group?: string }) => {
|
|||
? `${prefixPath}/document/detail/${list?.slug}`
|
||||
: `${prefixPath}/audio/detail/${list?.slug}`
|
||||
}
|
||||
className="absolute bottom-20 left-8 lg:left-32 z-20 text-white w-[85%] lg:w-[45%] cursor-pointer"
|
||||
className="absolute bottom-10 lg:bottom-20 left-8 lg:left-32 z-20 text-white w-[85%] lg:w-[45%] cursor-pointer"
|
||||
>
|
||||
<span className="text-red-600 text-lg font-bold uppercase">
|
||||
<span className="text-red-600 text-[10px] lg:text-lg font-bold uppercase">
|
||||
{list?.categoryName}
|
||||
</span>
|
||||
<h2 className="text-xl font-bold">{list?.title}</h2>
|
||||
<p className="text-sm mt-2">
|
||||
<h2 className="text-[14px] lg:text-xl font-bold">{list?.title}</h2>
|
||||
<p className="text-[9px] lg:text-sm mt-2">
|
||||
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
||||
{list?.timezone || "WIB"} | 👁 {list?.clickCount}
|
||||
</p>
|
||||
</Link>
|
||||
|
||||
{/* Tombol navigasi */}
|
||||
<div className="swiper-button-prev absolute left-6 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
||||
<ChevronLeft className="w-5 h-5" />
|
||||
<div className="absolute left-2 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
||||
<Icon
|
||||
icon="mdi:chevron-left"
|
||||
className="w-5 lg:w-10 h-5 lg:h-10"
|
||||
/>{" "}
|
||||
</div>
|
||||
<div className="swiper-button-next absolute right-6 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
||||
<ChevronRight className="w-5 h-5" />
|
||||
<div className="absolute right-2 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
||||
<Icon
|
||||
icon="mdi:chevron-right"
|
||||
className="w-5 lg:w-10 h-5 lg:h-10"
|
||||
/>{" "}
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { useTranslations } from "next-intl";
|
|||
import { Skeleton } from "../ui/skeleton";
|
||||
import Image from "next/image";
|
||||
import { motion } from "framer-motion";
|
||||
import ImageBlurry from "../ui/image-blurry";
|
||||
|
||||
const NewContent = (props: { group: string; type: string }) => {
|
||||
const [newContent, setNewContent] = useState<any>();
|
||||
|
|
@ -228,7 +229,7 @@ const NewContent = (props: { group: string; type: string }) => {
|
|||
}
|
||||
>
|
||||
{" "}
|
||||
<Image
|
||||
{/* <Image
|
||||
priority={true}
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
|
|
@ -238,6 +239,15 @@ const NewContent = (props: { group: string; type: string }) => {
|
|||
alt="image"
|
||||
src={image?.smallThumbnailLink}
|
||||
className="w-full h-full object-cover"
|
||||
/> */}
|
||||
<ImageBlurry
|
||||
src={image?.smallThumbnailLink}
|
||||
alt="image"
|
||||
priority={true}
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
)}`}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
|
@ -431,7 +441,7 @@ const NewContent = (props: { group: string; type: string }) => {
|
|||
prefixPath + `/video/detail/${video?.slug}`
|
||||
}
|
||||
>
|
||||
<Image
|
||||
{/* <Image
|
||||
priority={true}
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
|
|
@ -441,6 +451,15 @@ const NewContent = (props: { group: string; type: string }) => {
|
|||
height={1440}
|
||||
src={video?.smallThumbnailLink}
|
||||
className="w-full h-full object-cover"
|
||||
/> */}
|
||||
<ImageBlurry
|
||||
src={video?.smallThumbnailLink}
|
||||
alt="video"
|
||||
priority={true}
|
||||
placeholder={`data:image/svg+xml;base64,${toBase64(
|
||||
shimmer(700, 475)
|
||||
)}`}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { getHeroData } from "@/service/landing/landing";
|
|||
import { htmlToString } from "@/utils/globals";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { Button } from "../ui/button";
|
||||
import ImageBlurry from "../ui/image-blurry";
|
||||
|
||||
const ScrollableContent = () => {
|
||||
const [contentType, setContentType] = useState("all");
|
||||
|
|
@ -161,7 +162,10 @@ const ScrollableContent = () => {
|
|||
</div>
|
||||
<button
|
||||
onClick={() =>
|
||||
router.push(prefixPath + `/${contentType}/filter?title=${search.toLowerCase()}`)
|
||||
router.push(
|
||||
prefixPath +
|
||||
`/${contentType}/filter?title=${search.toLowerCase()}`
|
||||
)
|
||||
}
|
||||
className="flex justify-center items-center px-6 w-full lg:w-[20%] py-4 bg-[#bb3523] gap-2 text-white rounded-lg hover:bg-red-700 text-[14px]"
|
||||
>
|
||||
|
|
@ -206,12 +210,22 @@ const ScrollableContent = () => {
|
|||
: `${prefixPath}/audio/detail/${item?.slug}`
|
||||
}
|
||||
>
|
||||
<Image
|
||||
{/* <Image
|
||||
priority={true}
|
||||
src={item?.smallThumbnailLink}
|
||||
alt={item?.title}
|
||||
fill
|
||||
objectFit="cover"
|
||||
/> */}
|
||||
<ImageBlurry
|
||||
priority={true}
|
||||
src={item?.smallThumbnailLink}
|
||||
alt="gambar-utama"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute top-2 right-2 bg-[#c03724] rounded-full p-1 shadow">
|
||||
<svg
|
||||
|
|
@ -325,12 +339,22 @@ const ScrollableContent = () => {
|
|||
: `${prefixPath}/audio/detail/${item?.slug}`
|
||||
}
|
||||
>
|
||||
<Image
|
||||
{/* <Image
|
||||
priority={true}
|
||||
src={item?.smallThumbnailLink}
|
||||
alt={item?.title}
|
||||
fill
|
||||
objectFit="cover"
|
||||
/> */}
|
||||
<ImageBlurry
|
||||
priority={true}
|
||||
src={item?.smallThumbnailLink}
|
||||
alt="gambar-utama"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute top-2 right-2 bg-[#c03724] rounded-full p-1 shadow">
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -3,15 +3,22 @@ import React, { useEffect, useState } from "react";
|
|||
interface ImageBlurryProps {
|
||||
src: string;
|
||||
alt?: string;
|
||||
classname?: string;
|
||||
className?: string;
|
||||
key?: string | number;
|
||||
style?: React.CSSProperties;
|
||||
priority?: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
const ImageBlurry: React.FC<ImageBlurryProps> = (props) => {
|
||||
const { src, alt, classname, key, style, priority = false } = props;
|
||||
|
||||
const ImageBlurry: React.FC<ImageBlurryProps> = ({
|
||||
src,
|
||||
alt,
|
||||
className,
|
||||
key,
|
||||
style,
|
||||
priority = false,
|
||||
placeholder,
|
||||
}) => {
|
||||
const [imgSrc, setImgSrc] = useState<string>(src);
|
||||
const [isError, setIsError] = useState<boolean>(false);
|
||||
|
||||
|
|
@ -26,7 +33,7 @@ const ImageBlurry: React.FC<ImageBlurryProps> = (props) => {
|
|||
checkImage(pic);
|
||||
} else {
|
||||
pic.addEventListener("load", function () {
|
||||
checkImage(this);
|
||||
checkImage(this as HTMLImageElement);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -48,12 +55,10 @@ const ImageBlurry: React.FC<ImageBlurryProps> = (props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
// <div className="relative overflow-hidden bg-[#e9e9e9] blurry-wrapper">
|
||||
// <div className="absolute -top-6 -bottom-6 left-0 -right-6 bg-[#e9e9e9] blur-[8px] scale-[1.8] bg-center bg-no-repeat bg-contain blur-bg" style={{ backgroundImage: `url(${imgSrc})` }}></div>
|
||||
// <div className="absolute inset-0 bg-transparent bg-center bg-no-repeat bg-contain image-bg" style={{ backgroundImage: `url(${imgSrc})` }}></div>
|
||||
// </div>
|
||||
<div
|
||||
className="blurry-wrapper relative overflow-hidden bg-[#e9e9e9] rounded-t-lg"
|
||||
className={`blurry-wrapper relative overflow-hidden bg-[#e9e9e9] rounded-t-lg ${
|
||||
className || ""
|
||||
}`}
|
||||
key={key}
|
||||
style={style}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ export async function listSPIT(
|
|||
|
||||
export async function deleteBulkSPIT(ids: any) {
|
||||
const url = `media/spit/bulk?ids=${ids}`;
|
||||
return httpDeleteInterceptor( url );
|
||||
return httpDeleteInterceptor(url);
|
||||
}
|
||||
|
||||
export async function listNulisAI(limit: any, page: any, title: string = "") {
|
||||
|
|
@ -176,6 +176,11 @@ export async function createMedia(data: any) {
|
|||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function updateFilePlacements(data: any) {
|
||||
const url = "media/file/update-placement";
|
||||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function uploadThumbnail(id: any, data: any) {
|
||||
const url = `media/upload?id=${id}&operation=thumbnail`;
|
||||
const headers = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue