feat:embed tiktok facebook

This commit is contained in:
Rama Priyanto 2025-03-13 15:53:48 +07:00
parent 7d6c7ff927
commit f5fbf31eb9
9 changed files with 503 additions and 60 deletions

View File

@ -2654,3 +2654,34 @@ export const CopyIcon = ({
<path fill="none" d="M0 0h36v36H0z" />
</svg>
);
export const PlayIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
{...props}
viewBox="0 0 16 16"
>
<g fill="none">
<g clip-path="url(#gravityUiPlayFill0)">
<path
fill="currentColor"
fillRule="evenodd"
d="M14.756 10.164c1.665-.962 1.665-3.366 0-4.329L6.251.918C4.585-.045 2.5 1.158 2.5 3.083v9.834c0 1.925 2.085 3.128 3.751 2.164z"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="gravityUiPlayFill0">
<path fill="currentColor" d="M0 0h16v16H0z" />
</clipPath>
</defs>
</g>
</svg>
);

View File

@ -114,7 +114,7 @@ export default function MedolUpdate() {
<div className="text-xl font-semibold w-full ">
<p className="border-b-2 border-red-600 w-fit mx-auto">Media Update</p>
</div>
<div className=" p-1 md:py-5 md:px-12 space-y-5">
<div className=" p-1 md:py-5 md:px-5 space-y-5">
<Tabs
classNames={{
tabList: "bg-white shadow-lg border p-2 rounded-lg",
@ -148,21 +148,23 @@ export default function MedolUpdate() {
},
}}
pagination={true}
className="mySwiper"
className="mySwiper custom-button-slider !px-[8px]"
onSwiper={(swiper) => {
swiper.navigation.nextEl?.classList.add(
"bg-white/70",
"bg-white",
"!text-black",
"rounded-full",
"!w-[40px]",
"!h-[40px]"
"!h-[40px]",
"!border-2"
);
swiper.navigation.prevEl?.classList.add(
"bg-white/70",
"bg-white",
"!text-black",
"rounded-full",
"!w-[40px]",
"!h-[40px]"
"!h-[40px]",
"!border-2"
);
}}
>

View File

@ -28,9 +28,11 @@ import { useTranslations } from "next-intl";
import TwitterWidget from "../ui/social-media/twitter";
import InstagramWidget from "../ui/social-media/instagram";
import YoutubeWidget from "../ui/social-media/youtube";
import FacebookWidgetNew from "../ui/social-media/facebook-new";
import TiktokWidget from "../ui/social-media/tiktok";
export default function SocialMediaNew() {
const [selectedTab, setSelectedTab] = useState<any>("x");
const [selectedTab, setSelectedTab] = useState<any>("fb");
const [mediahubUpdate, setMediahubUpdate] = useState<any>([]);
const [tbnUpdate, setTbnUpdate] = useState([]);
const [inpUpdate, setInpUpdate] = useState([]);
@ -68,6 +70,12 @@ export default function SocialMediaNew() {
<Tab key="yt" title="Youtube">
<YoutubeWidget />
</Tab>
<Tab key="fb" title="Facebook">
<FacebookWidgetNew />
</Tab>
<Tab key="tiktok" title="Tiktok">
<TiktokWidget />
</Tab>
</Tabs>
</div>
</div>

View File

@ -0,0 +1,154 @@
"use client";
import { facebookHumasData } from "@/service/generate-article";
import {
Button,
Card,
CardBody,
CardFooter,
Image,
Modal,
ModalBody,
ModalContent,
ModalHeader,
Spinner,
useDisclosure,
} from "@heroui/react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import { Navigation, Pagination } from "swiper/modules";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import { FacebookIcon, FacebookLandingIcon } from "@/components/icons";
import { textEllipsis } from "@/utils/global";
import FacebookEmbed from "./facebook";
dayjs.extend(relativeTime);
const formatRelativeTime = (dateString: string): string => {
return dayjs(dateString, "DD MMM YYYY HH:mm:ss").fromNow();
};
export default function FacebookWidgetNew() {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const [selected, setSelected] = useState("");
const [facebookData, setFacebookData] = useState<any>([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const res = await facebookHumasData();
setFacebookData(res?.data?.data);
};
return facebookData?.length > 1 ? (
<div className="flex flex-col gap-4">
<div className="flex justify-between w-full px-2 items-center">
<div className="flex flex-row gap-2 items-center">
<Image src="/divhumas.png" alt="humas-polri" className="!w-[68px]" />{" "}
<p className="font-semibold">Divisi Humas Polri</p>
</div>
<Link target="_blank" href="https://www.facebook.com/DivHumasPolri">
<Button radius="sm" color="primary">
Follow us
</Button>
</Link>
</div>
<Swiper
navigation={true}
modules={[Navigation]}
spaceBetween={10}
slidesPerView={1}
breakpoints={{
// When the window width is less than 640px
720: {
slidesPerView: 3, // Set slidesPerView to 1 on mobile
},
1080: {
slidesPerView: 4,
},
}}
pagination={true}
className="mySwiper custom-button-slider !px-[8px]"
onSwiper={(swiper) => {
swiper.navigation.nextEl?.classList.add(
"bg-white",
"!text-black",
"rounded-full",
"!w-[40px]",
"!h-[40px]",
"!border-2"
);
swiper.navigation.prevEl?.classList.add(
"bg-white",
"!text-black",
"rounded-full",
"!w-[40px]",
"!h-[40px]",
"!border-2"
);
}}
>
{facebookData?.map((data: any) => (
<SwiperSlide
key={data.id}
className="hover:shadow-xl hover:opacity-90"
>
<Card
isPressable
shadow="none"
className=" bg-white text-black border-2 w-full"
onPress={() => {
setSelected(data.url);
onOpen();
}}
>
<CardBody className="overflow-visible px-5 lg:py-10 text-center flex flex-col gap-2 justify-center items-center text-sm">
<FacebookLandingIcon size={24} />
<p>{textEllipsis(data.text, 200)}</p>
<div className="flex flex-col gap-1">
{" "}
<p className="font-bold">Divisi Humas Polri</p>
<p className="text-xs">
{formatRelativeTime(data.publishedDate)}
</p>
</div>
<Image
src="/divhumas.png"
alt="humas-polri"
className="!w-[48px]"
/>
</CardBody>
</Card>
</SwiperSlide>
))}
</Swiper>
<Modal
isOpen={isOpen}
onOpenChange={onOpenChange}
size="xl"
placement="center"
>
<ModalContent>
{(onClose) => (
<>
<ModalBody>
<FacebookEmbed postUrl={selected} />
</ModalBody>
</>
)}
</ModalContent>
</Modal>
</div>
) : (
<div className="w-full flex justify-center items-center">
<Spinner size="lg" className="mx-auto" color="default" />
</div>
);
}

View File

@ -1,42 +1,52 @@
import { useEffect } from "react";
"use client";
import { Spinner } from "@heroui/react";
import { useEffect, useRef, useState } from "react";
interface FacebookEmbedProps {
postUrl: string;
width?: number;
showText?: boolean;
}
declare global {
interface Window {
FB: any;
}
}
const FacebookEmbed: React.FC<FacebookEmbedProps> = ({ postUrl }) => {
const [loading, setLoading] = useState(true);
const FacebookWidget = () => {
useEffect(() => {
if (typeof window !== "undefined") {
if (!window.FB) {
const script = document.createElement("script");
script.src = "https://embedsocial.com/cdn/ht.js";
script.src =
"https://connect.facebook.net/id_ID/sdk.js#xfbml=1&version=v22.0&appId=1290603775136204";
script.async = true;
document.head.appendChild(script);
script.defer = true;
document.body.appendChild(script);
return () => {
document.head.removeChild(script);
script.onload = () => {
window.FB?.XFBML.parse();
setLoading(false);
};
} else {
window.FB.XFBML.parse();
setLoading(false);
}
}, []);
return (
// <div
// className="embedsocial-hashtag rounded-md"
// data-ref="7501cb8270327e936909c6ed57298ead0c5c07d0"
// ></div>
<iframe
name="f3d8479283c8374"
width="1080px"
height="480px"
data-testid="fb:page Facebook Social Plugin"
title="fb:page Facebook Social Plugin"
scrolling="no"
allow="encrypted-media"
src="https://web.facebook.com/v12.0/plugins/page.php?adapt_container_width=true&amp;app_id=664876294862028&amp;channel=https%3A%2F%2Fstaticxx.facebook.com%2Fx%2Fconnect%2Fxd_arbiter%2F%3Fversion%3D46%23cb%3Df23357a43cf8c%26domain%3Dhumas.polri.go.id%26is_canvas%3Dfalse%26origin%3Dhttps%253A%252F%252Fhumas.polri.go.id%252Ff29b91925fa28f%26relation%3Dparent.parent&amp;container_width=280&amp;height=2080&amp;hide_cover=true&amp;href=https%3A%2F%2Fwww.facebook.com%2FDivHumasPolri&amp;locale=id_ID&amp;sdk=joey&amp;show_facepile=false&amp;small_header=false&amp;tabs=timeline&amp;width=280"
style={{
border: "none",
visibility: "visible",
width: "100%",
height: "588px",
borderRadius: "15px",
}}
></iframe>
<>
{loading && <Spinner />}
<div
className={`fb-post ${loading ? "hidden" : ""}`}
data-href={postUrl}
data-width="500"
data-show-text="true"
></div>
</>
);
};
export default FacebookWidget;
export default FacebookEmbed;

View File

@ -1,29 +1,187 @@
import { useEffect } from 'react';
"use client";
import { tiktokHumasData } from "@/service/generate-article";
import {
Button,
Card,
CardBody,
CardFooter,
Image,
Spinner,
} from "@heroui/react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import { Navigation, Pagination } from "swiper/modules";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import { PlayIcon, TiktokLandingIcon } from "@/components/icons";
import { textEllipsis } from "@/utils/global";
const TiktokWidget = () => {
const getRelativeTime = (timestamp: number): string => {
const now = Date.now();
const timeDiff = now - timestamp * 1000; // Konversi ke milidetik
useEffect(() => {
const seconds = Math.floor(timeDiff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const weeks = Math.floor(days / 7);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);
if (typeof window !== 'undefined') {
const script = document.createElement('script');
script.src = "https://embedsocial.com/cdn/ht.js";
script.async = true;
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
if (seconds < 60) return "just now";
if (minutes < 60) return `${minutes} min ago`;
if (hours < 24) return `${hours} hour${hours > 1 ? "s" : ""} ago`;
if (days < 7) return `${days} day${days > 1 ? "s" : ""} ago`;
if (weeks < 4) return `${weeks} week${weeks > 1 ? "s" : ""} ago`;
if (months < 12) return `${months} month${months > 1 ? "s" : ""} ago`;
return `${years} year${years > 1 ? "s" : ""} ago`;
};
}
export default function TiktokWidget() {
const [tiktokData, setTiktokData] = useState<any>([]);
useEffect(() => {
fetchData();
}, []);
return (
<div className="embedsocial-hashtag rounded-md" data-ref="ad29d80894ddd211d09a0c63c53c97d29ff66359"></div>
);
const fetchData = async () => {
const res = await tiktokHumasData();
setTiktokData(res?.data?.data);
};
export default TiktokWidget;
const [isVideoLoaded, setIsVideoLoaded] = useState(0);
const videoUrl = "https://www.tiktok.com/@divhumaspolriofficial/video/";
const loadTikTokVideo = (id: number) => {
setIsVideoLoaded(id);
// Tambahkan script TikTok embed setelah video dimuat
const script = document.createElement("script");
script.src = "https://www.tiktok.com/embed.js";
script.async = true;
document.body.appendChild(script);
};
const tiktokPlayer = (data: any) => {
return (
<div>
{isVideoLoaded !== data.id ? (
<a
onClick={() => loadTikTokVideo(data.id)}
className="relative flex flex-col gap-2 justify-center items-center lg:py-10 px-5 transition-all duration-300 hover:bg-black/10 h-[756px] active:bg-black/20"
>
<TiktokLandingIcon size={30} />
<p>{textEllipsis(data.desc, 200)}</p>
<div className="flex flex-col gap-1">
<p className="font-bold">Divisi Humas Polri</p>
<p className="text-xs">{getRelativeTime(data.create_time)}</p>
</div>
<Image
src="/divhumas.png"
alt="humas-polri"
className="!w-[48px]"
/>
<div
className="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity duration-300"
onClick={(e) => {
e.stopPropagation(); // Mencegah masalah klik tertangkap oleh elemen lain
loadTikTokVideo(data.id);
}}
>
<button className="bg-black/50 text-white rounded-full p-3">
<PlayIcon />
</button>
</div>
</a>
) : (
<blockquote
className="tiktok-embed !my-0"
cite={videoUrl + String(data.id)}
data-video-id={data.id}
style={{ minWidth: "325px" }}
>
<section></section>
</blockquote>
)}
</div>
);
};
return tiktokData?.length > 1 ? (
<div className="flex flex-col gap-4">
<div className="flex justify-between w-full px-2 items-center">
<div className="flex flex-row gap-2 items-center">
<Image src="/divhumas.png" alt="humas-polri" className="!w-[68px]" />{" "}
<p className="font-semibold">Divisi Humas Polri</p>
</div>
<Link
target="_blank"
href="https://www.tiktok.com/@divhumaspolriofficial"
>
<Button radius="sm" color="primary">
Follow us
</Button>
</Link>
</div>
<Swiper
navigation={true}
modules={[Navigation]}
spaceBetween={10}
slidesPerView={1}
breakpoints={{
// When the window width is less than 640px
720: {
slidesPerView: 3, // Set slidesPerView to 1 on mobile
},
1080: {
slidesPerView: 4,
},
}}
pagination={true}
className="mySwiper custom-button-slider !px-[8px]"
onSwiper={(swiper) => {
swiper.navigation.nextEl?.classList.add(
"bg-white",
"!text-black",
"rounded-full",
"!w-[40px]",
"!h-[40px]",
"!border-2"
);
swiper.navigation.prevEl?.classList.add(
"bg-white",
"!text-black",
"rounded-full",
"!w-[40px]",
"!h-[40px]",
"!border-2"
);
}}
>
{tiktokData?.map((data: any) => (
<SwiperSlide
key={data.id}
className="hover:shadow-xl hover:opacity-90"
>
<Card
isPressable
shadow="none"
className=" bg-white text-black border-2 w-full"
>
<CardBody className="!p-0 overflow-visible text-center flex flex-col justify-center items-center text-sm">
{tiktokPlayer(data)}
</CardBody>
</Card>
</SwiperSlide>
))}
</Swiper>
</div>
) : (
<div className="w-full flex justify-center items-center">
<Spinner size="lg" className="mx-auto" color="default" />
</div>
);
}

View File

@ -1,4 +1,8 @@
import { httpGet, httpPost } from "@/services/http-config/disestages-services";
import {
httpGet,
httpPost,
httpPost2,
} from "@/services/http-config/disestages-services";
interface GenerateKeywordsAndTitleRequestData {
keyword: string;
@ -224,3 +228,40 @@ export async function saveManualContext(data: any) {
};
return await httpPost("ai-writer/create-article", headers, data);
}
export async function facebookHumasData() {
const data = {
monitoringId: "f33b666c-ac07-4cd6-a64e-200465919e8c",
page: 133,
limit: 10,
userId: "0qrwedt9EcuLyOiBUbqhzjd0BwGRjDBd",
};
const headers = {
"content-type": "application/json",
Authorization:
"Basic bmdETFBQaW9ycGx6bncyalRxVmUzWUZDejV4cUtmVUo6UHJEaERXUmNvdkJSNlc1Sg==",
};
return await httpPost2(
"monitoring-media-social/datatable/facebook",
headers,
data
);
}
export async function tiktokHumasData() {
const data = {
monitoringId: "1e301867-9599-4d82-ab57-9f7931f96903",
page: 1,
limit: 10,
userId: "0qrwedt9EcuLyOiBUbqhzjd0BwGRjDBd",
};
const headers = {
"content-type": "application/json",
Authorization:
"Basic bmdETFBQaW9ycGx6bncyalRxVmUzWUZDejV4cUtmVUo6UHJEaERXUmNvdkJSNlc1Sg==",
};
return await httpPost2(
"monitoring-media-social/datatable/tiktok",
headers,
data
);
}

View File

@ -1,4 +1,6 @@
import axiosDisestagesInstance from "./disestages-instance";
import axios from "axios";
const baseURL = "https://staging.disestages.com/api";
export async function httpPost(pathUrl: any, headers: any, data?: any) {
const response = await axiosDisestagesInstance
@ -45,3 +47,32 @@ export async function httpGet(pathUrl: any, headers: any) {
};
}
}
export async function httpPost2(pathUrl: any, headers: any, data?: any) {
const response = await axios
.create({
baseURL,
headers: {
"content-type": "application/json",
},
})
.post(pathUrl, data, { headers })
.catch(function (error) {
console.log(error);
return error.response;
});
console.log("Response base svc : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}

View File

@ -53,6 +53,14 @@
align-items: center;
}
.custom-button-slider .swiper-button-next {
right: 0px !important;
}
.custom-button-slider .swiper-button-prev {
left: 0px !important;
}
.swiper-slide img {
display: block;
width: 100%;
@ -62,13 +70,13 @@
.swiper-button-next::after,
.swiper-button-prev::after {
font-size: 28px !important;
font-size: 20px !important;
}
@media (max-width: 768px) {
.swiper-button-next::after,
.swiper-button-prev::after {
font-size: 14px !important;
font-size: 10px !important;
}
}