2026-02-21 19:04:54 +00:00

617 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useEffect, useState, useCallback } from "react";
import { useRouter } from "next/navigation";
import axios from "axios";
import { ApiServerBaseUrl } from "@/utils/baseurl.utils";
import { getSocialAuthStatus } from "@/utils/commonFunction.utils";
import {
Instagram,
CheckCircle,
XCircle,
Link2,
Users,
Image as ImageIcon,
Globe,
BarChart3,
Shield,
Zap,
ArrowRight,
ExternalLink,
RefreshCw
} from "lucide-react";
import Link from "next/link";
interface Channel {
_id: string;
name: string;
id?: string;
has_linked_account: boolean;
}
const SocialMediaChannels = () => {
const router = useRouter();
const [channels, setChannels] = useState<Channel[]>([]);
const [loading, setLoading] = useState(true);
const [openModal, setOpenModal] = useState(false);
const [successMsg, setSuccessMsg] = useState("");
const [connectedChannel, setConnectedChannel] = useState<string | null>(null);
const [accountData, setAccountData] = useState<any>(null);
const [refreshing, setRefreshing] = useState(false);
// ---------------------------------------------
// VALIDATE USER ACCESS
// ---------------------------------------------
useEffect(() => {
async function validate() {
const userEmail = localStorage.getItem("user_email");
if (!userEmail) {
router.push("/social-media-connect");
return;
}
const storedUser = localStorage.getItem("userDetails");
if (!storedUser) {
router.push("/social-media-connect");
return;
}
const user = JSON.parse(storedUser);
const role = user?.role;
if (role === "customer") {
const session = localStorage.getItem("payment_session");
if (!session) {
router.push("/pricing");
return;
}
}
const res = await getSocialAuthStatus(userEmail);
if (!res?.connected) {
router.push("/social-media-connect");
return;
}
}
validate();
}, [router]);
// ---------------------------------------------
// FETCH CHANNELS
// ---------------------------------------------
const loadChannels = useCallback(async () => {
try {
const user = localStorage.getItem("user_email");
const res = await axios.get(`${ApiServerBaseUrl}/social/channels?userId=${user}`);
const data: Channel[] = res.data || [];
setChannels(data);
} catch (err) {
console.error("Failed to load channels", err);
} finally {
setLoading(false);
}
}, []);
// ---------------------------------------------
// FETCH ACCOUNT DETAILS
// ---------------------------------------------
const loadAccountDetails = useCallback(async () => {
try {
const user = localStorage.getItem("user_email");
const connected = localStorage.getItem("connectedChannel");
if (!user || !connected) return;
const res = await axios.get(`${ApiServerBaseUrl}/social/account?userId=${user}`);
setAccountData(res.data || null);
} catch (err: any) {
if (err.response?.data?.error !== "Connect a channel first") {
console.error("Failed to load account details", err);
}
setAccountData(null);
}
}, []);
useEffect(() => {
loadChannels();
const saved = localStorage.getItem("connectedChannel");
if (saved) {
setConnectedChannel(saved);
loadAccountDetails();
}
}, [loadChannels, loadAccountDetails]);
// ---------------------------------------------
// CONNECT CHANNEL
// ---------------------------------------------
const handleConnectChannel = async (channelId: string, channelName: string) => {
try {
const user = localStorage.getItem("user_email");
const res = await axios.post(
`${ApiServerBaseUrl}/social/connect?userId=${user}`,
{ channel_id: channelId }
);
const savedId = res.data?.data?.channel_id || channelId;
localStorage.setItem("connectedChannel", savedId);
setConnectedChannel(savedId);
setSuccessMsg(`${channelName} connected successfully!`);
setOpenModal(false);
await loadAccountDetails();
} catch (err: any) {
setSuccessMsg(err.response?.data?.error || `Failed to connect ${channelName}`);
}
};
// ---------------------------------------------
// REMOVE CHANNEL
// ---------------------------------------------
const handleRemoveChannel = () => {
localStorage.removeItem("connectedChannel");
setConnectedChannel(null);
setAccountData(null);
setSuccessMsg("Channel removed successfully!");
};
// ---------------------------------------------
// REFRESH DATA
// ---------------------------------------------
const handleRefresh = async () => {
setRefreshing(true);
await loadAccountDetails();
setTimeout(() => setRefreshing(false), 1000);
};
return (
<div className="min-h-screen bg-gradient-to-b from-[#0a0a0a] to-[#111111] p-4 md:p-6 relative overflow-x-hidden">
{/* Floating background bubbles */}
<div className="absolute top-20 left-[5%] w-24 h-24 rounded-full bg-pink-500/20 blur-xl animate-float"></div>
<div className="absolute top-40 right-[10%] w-32 h-32 rounded-full bg-blue-500/15 blur-xl animate-float-slow"></div>
<div className="absolute bottom-40 left-[20%] w-20 h-20 rounded-full bg-purple-500/20 blur-xl animate-float"></div>
<div className="absolute bottom-20 right-[15%] w-28 h-28 rounded-full bg-cyan-500/15 blur-xl animate-float-slow"></div>
{/* Success Message */}
{successMsg && (
<div className="fixed top-4 right-4 z-50">
<div className={`px-6 py-4 rounded-xl backdrop-blur-xl border ${
successMsg.includes("success")
? "bg-green-900/30 border-green-500/30 text-green-400"
: "bg-red-900/30 border-red-500/30 text-red-400"
}`}>
<div className="flex items-center gap-3">
{successMsg.includes("success") ? (
<CheckCircle className="w-5 h-5" />
) : (
<XCircle className="w-5 h-5" />
)}
<span>{successMsg}</span>
<button
onClick={() => setSuccessMsg("")}
className="ml-4 text-gray-400 hover:text-white"
>
×
</button>
</div>
</div>
</div>
)}
{/* Main Container */}
<div className="max-w-6xl mx-auto relative z-10">
{/* Header */}
<div className="mb-10">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8">
<div className="flex items-center gap-4">
<div className="relative">
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-pink-500 to-purple-600 flex items-center justify-center shadow-lg shadow-pink-500/30">
<Instagram className="w-8 h-8 text-white" />
</div>
{connectedChannel && (
<div className="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-green-500 border-2 border-[#111111] flex items-center justify-center">
<div className="w-2 h-2 rounded-full bg-white"></div>
</div>
)}
</div>
<div>
<h1 className="text-3xl md:text-4xl font-bold text-white">
Instagram Channels
</h1>
<p className="text-gray-300 mt-1">
Manage and connect your Instagram accounts
</p>
</div>
</div>
{/* Action Buttons */}
<div className="flex items-center gap-4">
{connectedChannel && accountData && (
<button
onClick={handleRefresh}
disabled={refreshing}
className="px-4 py-2 rounded-xl bg-white/5 hover:bg-white/10 border border-white/10 text-gray-300 hover:text-white transition-all duration-300 flex items-center gap-2"
>
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin' : ''}`} />
Refresh
</button>
)}
{!connectedChannel && (
<button
onClick={() => setOpenModal(true)}
className="px-6 py-3 rounded-xl bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-700 hover:to-purple-700 text-white font-semibold transition-all duration-300 hover:scale-105 flex items-center gap-2"
>
<Link2 className="w-5 h-5" />
Connect Channel
</button>
)}
</div>
</div>
</div>
{/* Main Content */}
{connectedChannel && accountData ? (
<div className="grid lg:grid-cols-3 gap-8">
{/* Left Column - Profile */}
<div className="lg:col-span-2 space-y-8">
{/* Profile Card */}
<div className="bg-gradient-to-br from-gray-900/90 to-black/90 backdrop-blur-xl rounded-2xl p-8 border border-white/10 shadow-2xl">
<div className="flex flex-col md:flex-row items-center md:items-start gap-8">
{/* Profile Image */}
<div className="relative">
<img
src={accountData.profile_picture_url}
alt={accountData.username || "Instagram profile"}
className="w-32 h-32 rounded-2xl object-cover border-2 border-white/20 shadow-lg"
/>
<div className="absolute -bottom-2 -right-2 w-10 h-10 rounded-full bg-gradient-to-r from-pink-500 to-purple-600 flex items-center justify-center">
<Instagram className="w-5 h-5 text-white" />
</div>
</div>
{/* Profile Info */}
<div className="flex-1">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
<div>
<h2 className="text-2xl font-bold text-white">
{accountData.username}
</h2>
{accountData.name && (
<p className="text-gray-400">{accountData.name}</p>
)}
</div>
<div className="flex items-center gap-3">
<span className="px-3 py-1 rounded-full bg-green-500/20 text-green-400 text-sm font-medium border border-green-500/30">
Connected
</span>
</div>
</div>
{/* Stats */}
<div className="grid grid-cols-3 gap-6 mb-6">
<div className="text-center">
<div className="text-3xl font-bold text-white">{accountData.media_count}</div>
<div className="text-gray-400 text-sm">Posts</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-white">{accountData.followers_count}</div>
<div className="text-gray-400 text-sm">Followers</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-white">{accountData.follows_count}</div>
<div className="text-gray-400 text-sm">Following</div>
</div>
</div>
{/* Bio */}
{accountData.biography && (
<div className="mb-6">
<h3 className="text-white font-semibold mb-2">Bio</h3>
<p className="text-gray-300 whitespace-pre-line">{accountData.biography}</p>
</div>
)}
{/* Website */}
{accountData.website && (
<a
href={accountData.website}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-blue-400 hover:text-blue-300 transition-colors"
>
<Globe className="w-4 h-4" />
<span>{accountData.website}</span>
<ExternalLink className="w-3 h-3" />
</a>
)}
</div>
</div>
</div>
{/* Quick Actions */}
<div className="bg-gradient-to-br from-gray-900/90 to-black/90 backdrop-blur-xl rounded-2xl p-6 border border-white/10 shadow-2xl">
<h3 className="text-xl font-bold text-white mb-6 flex items-center gap-3">
<Zap className="w-5 h-5 text-yellow-400" />
Quick Actions
</h3>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<Link
href="/social-media-posts"
className="p-4 rounded-xl bg-white/5 hover:bg-white/10 border border-white/10 transition-all duration-300 hover:scale-105 group"
>
<div className="flex items-center gap-3 mb-2">
<ImageIcon className="w-6 h-6 text-pink-400" />
<span className="text-white font-medium">Media Library</span>
</div>
<p className="text-gray-400 text-sm">View and manage your posts</p>
</Link>
<Link
href="/comments"
className="p-4 rounded-xl bg-white/5 hover:bg-white/10 border border-white/10 transition-all duration-300 hover:scale-105 group"
>
<div className="flex items-center gap-3 mb-2">
<Users className="w-6 h-6 text-blue-400" />
<span className="text-white font-medium">Comments</span>
</div>
<p className="text-gray-400 text-sm">Manage comments & engagement</p>
</Link>
<Link
href="/insights"
className="p-4 rounded-xl bg-white/5 hover:bg-white/10 border border-white/10 transition-all duration-300 hover:scale-105 group"
>
<div className="flex items-center gap-3 mb-2">
<BarChart3 className="w-6 h-6 text-green-400" />
<span className="text-white font-medium">Insights</span>
</div>
<p className="text-gray-400 text-sm">View performance analytics</p>
</Link>
</div>
</div>
</div>
{/* Right Column - Settings & Info */}
<div className="space-y-8">
{/* Connection Status */}
<div className="bg-gradient-to-br from-gray-900/90 to-black/90 backdrop-blur-xl rounded-2xl p-6 border border-white/10 shadow-2xl">
<h3 className="text-xl font-bold text-white mb-4">Channel Status</h3>
<div className="space-y-6">
<div className="flex items-center justify-between p-4 rounded-xl bg-white/5">
<div className="flex items-center gap-3">
<Instagram className="w-6 h-6 text-pink-400" />
<span className="text-white">Instagram Account</span>
</div>
<div className="px-3 py-1 rounded-full text-sm font-medium bg-green-500/20 text-green-400 border border-green-500/30">
Connected
</div>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-400">Media Access</span>
<span className="text-green-400"> Available</span>
</div>
<div className="flex items-center justify-between text-sm">
<span className="text-gray-400">Comment Access</span>
<span className="text-green-400"> Available</span>
</div>
<div className="flex items-center justify-between text-sm">
<span className="text-gray-400">Insights Access</span>
<span className="text-green-400"> Available</span>
</div>
</div>
<div className="pt-4 border-t border-white/10">
<button
onClick={handleRemoveChannel}
className="w-full py-3 rounded-xl bg-gradient-to-r from-red-600 to-red-700 hover:from-red-700 hover:to-red-800 text-white font-medium transition-all duration-300 hover:scale-105 flex items-center justify-center gap-2"
>
<Link2 className="w-4 h-4" />
Disconnect Channel
</button>
<p className="text-gray-400 text-xs text-center mt-2">
This will revoke Social Buddy's access to this account
</p>
</div>
</div>
</div>
{/* Info Card */}
<div className="bg-gradient-to-br from-blue-900/20 to-indigo-900/20 rounded-2xl p-6 border border-blue-500/30 backdrop-blur-xl">
<h4 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
<Shield className="w-5 h-5 text-blue-400" />
Channel Information
</h4>
<ul className="space-y-3 text-sm">
<li className="flex items-start gap-2 text-gray-300">
<CheckCircle className="w-4 h-4 text-green-400 flex-shrink-0 mt-0.5" />
<span>All data is synced securely</span>
</li>
<li className="flex items-start gap-2 text-gray-300">
<CheckCircle className="w-4 h-4 text-green-400 flex-shrink-0 mt-0.5" />
<span>Manual management only</span>
</li>
<li className="flex items-start gap-2 text-gray-300">
<CheckCircle className="w-4 h-4 text-green-400 flex-shrink-0 mt-0.5" />
<span>Real-time data updates</span>
</li>
</ul>
</div>
</div>
</div>
) : (
/* No Channel Connected State */
<div className="flex flex-col items-center justify-center py-20">
<div className="w-32 h-32 rounded-2xl bg-gradient-to-br from-gray-900 to-black border border-white/10 flex items-center justify-center mb-8">
<Instagram className="w-16 h-16 text-gray-400" />
</div>
<h2 className="text-2xl font-bold text-white mb-4">No Channel Connected</h2>
<p className="text-gray-400 text-center max-w-md mb-8">
Connect an Instagram account to access media management, insights, and comment tools.
</p>
<button
onClick={() => setOpenModal(true)}
className="px-8 py-4 rounded-xl bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-700 hover:to-purple-700 text-white font-semibold text-lg transition-all duration-300 hover:scale-105 flex items-center gap-3"
>
<Link2 className="w-6 h-6" />
Connect Instagram Channel
<ArrowRight className="w-5 h-5" />
</button>
</div>
)}
</div>
{/* Channel Selection Modal */}
{openModal && (
<div className="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center justify-center p-4 z-50">
<div className="bg-gradient-to-br from-gray-900 to-black backdrop-blur-xl rounded-2xl border border-white/10 shadow-2xl w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
{/* Modal Header */}
<div className="p-6 border-b border-white/10">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-white">Connect Instagram Channel</h2>
<p className="text-gray-400 mt-1">Select an Instagram account to connect</p>
</div>
<button
onClick={() => setOpenModal(false)}
className="w-8 h-8 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center text-gray-400 hover:text-white transition-colors"
>
×
</button>
</div>
</div>
{/* Modal Content */}
<div className="p-6 flex-1 overflow-y-auto">
{loading ? (
<div className="flex items-center justify-center py-20">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-pink-500"></div>
</div>
) : channels.length === 0 ? (
<div className="text-center py-20">
<Instagram className="w-16 h-16 text-gray-400 mx-auto mb-4" />
<p className="text-gray-400">No Instagram channels available</p>
<p className="text-gray-500 text-sm mt-2">Make sure your Instagram account is linked to Facebook</p>
</div>
) : (
<div className="grid gap-6 grid-cols-1 md:grid-cols-2">
{channels.map((channel) => (
<div
key={channel.id}
className="bg-gradient-to-br from-gray-900/50 to-black/50 backdrop-blur-sm rounded-2xl p-6 border border-white/10 hover:border-pink-500/30 transition-all duration-300 hover:scale-[1.02] group"
>
<div className="flex items-center gap-4 mb-4">
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
channel.has_linked_account
? "bg-gradient-to-br from-pink-500/20 to-purple-600/20"
: "bg-gray-800/50"
}`}>
<Instagram className={`w-6 h-6 ${
channel.has_linked_account ? "text-pink-400" : "text-gray-400"
}`} />
</div>
<div>
<h3 className="text-xl font-bold text-white">{channel.name}</h3>
<div className={`mt-1 text-sm ${
channel.has_linked_account
? "text-green-400 flex items-center gap-1"
: "text-orange-400 flex items-center gap-1"
}`}>
{channel.has_linked_account ? (
<>
<CheckCircle className="w-4 h-4" />
Ready to connect
</>
) : (
<>
<XCircle className="w-4 h-4" />
Requires authorization
</>
)}
</div>
</div>
</div>
<p className="text-gray-400 text-sm mb-6">
{channel.has_linked_account
? "Connect this Instagram account to access media, insights, and comments."
: "This account needs to be authorized through Facebook first."
}
</p>
<button
onClick={() => channel.has_linked_account && handleConnectChannel(channel.id!, channel.name)}
disabled={!channel.has_linked_account}
className={`w-full py-3 rounded-xl font-semibold transition-all duration-300 flex items-center justify-center gap-2 ${
channel.has_linked_account
? "bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-700 hover:to-purple-700 text-white hover:scale-105"
: "bg-gray-800 text-gray-500 cursor-not-allowed"
}`}
>
{channel.has_linked_account ? (
<>
<Link2 className="w-5 h-5" />
Connect Channel
<ArrowRight className="w-4 h-4" />
</>
) : (
"Authorization Required"
)}
</button>
</div>
))}
</div>
)}
</div>
{/* Modal Footer */}
<div className="p-6 border-t border-white/10">
<div className="flex justify-between items-center">
<p className="text-gray-400 text-sm">
{channels.length} channel{channels.length !== 1 ? 's' : ''} available
</p>
<button
onClick={() => setOpenModal(false)}
className="px-6 py-2 rounded-xl bg-white/10 hover:bg-white/20 text-gray-300 hover:text-white font-medium transition-colors"
>
Cancel
</button>
</div>
</div>
</div>
</div>
)}
{/* Animation Styles */}
<style jsx>{`
@keyframes float {
0%, 100% {
transform: translateY(0) translateX(0);
}
50% {
transform: translateY(-20px) translateX(10px);
}
}
@keyframes float-slow {
0%, 100% {
transform: translateY(0) translateX(0);
}
50% {
transform: translateY(-30px) translateX(-15px);
}
}
.animate-float {
animation: float 8s ease-in-out infinite;
}
.animate-float-slow {
animation: float-slow 10s ease-in-out infinite;
}
`}</style>
</div>
);
};
export default SocialMediaChannels;