profiles done
This commit is contained in:
parent
3f4d8167e3
commit
35fc65f3de
|
|
@ -5,7 +5,7 @@ import "./globals.css";
|
|||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "CallFi Dashboard",
|
||||
title: "CallFi",
|
||||
description: "Call out next big mover and win!",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -276,6 +276,6 @@ export default function App() {
|
|||
return (
|
||||
<PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || 'clxc7nmy906ifhis11rkovoto'}>
|
||||
<Home />
|
||||
</PrivyProvider>
|
||||
</PrivyProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,140 +1,208 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import Header from '@/components/Header';
|
||||
import Footer from '@/components/Footer';
|
||||
import Head from 'next/head'
|
||||
|
||||
import UserDetailModal from '@/components/UserDetailsModal';
|
||||
import { motion } from 'framer-motion';
|
||||
import CustomHeader from '@/components/CustomHeader';
|
||||
import { FaArrowDown, FaArrowUp, FaIcons, FaShare } from 'react-icons/fa';
|
||||
import { FaArrowDown, FaArrowUp, FaShare } from 'react-icons/fa';
|
||||
import { FaArrowLeft } from 'react-icons/fa6';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import CalloutCard from '@/components/CalloutCard';
|
||||
import { PrivyProvider, usePrivy } from '@privy-io/react-auth';
|
||||
import ShareModal from '@/components/ShareModal';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface UserDetail {
|
||||
tag: string;
|
||||
img_url: string;
|
||||
email: string;
|
||||
points: string;
|
||||
joined_date: string;
|
||||
calloutCount: number;
|
||||
// Add more fields as necessary
|
||||
}
|
||||
function UserPage({ params }: { params: { id: string } }) {
|
||||
const { login, user, ready, logout, linkTwitter, unlinkTwitter, linkWallet, unlinkWallet } = usePrivy();
|
||||
|
||||
interface Callout {
|
||||
price_at_creation: string;
|
||||
froze_price: string;
|
||||
gains: string;
|
||||
ca: string;
|
||||
id: string;
|
||||
icon_url: string;
|
||||
}
|
||||
|
||||
export default function UserPage({ params }: { params: { id: string } }) {
|
||||
const [userDetail, setUserDetail] = useState<UserDetail | null>(null);
|
||||
const [callouts, setCallouts] = useState<Callout[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [activeSelected, setActiveSelected] = useState(true);
|
||||
const [expandedInfo, setExpandedInfo] = useState(false);
|
||||
const [weekSelected, SetWeekSelected] = useState(true);
|
||||
const [allTimeSelected, SetAllTimeSelected] = useState(true);
|
||||
|
||||
|
||||
const [selectedPeriod, setSelectedPeriod] = useState("all time");
|
||||
const [userData, setUserData] = useState<any>(null);
|
||||
const [callouts, setCallouts] = useState<any[]>([]);
|
||||
const [history, setHistory] = useState<any[]>([]);
|
||||
const [loginAlert, setLoginAlert] = useState<string | null>(null);
|
||||
const [dexUrls, setDexUrls] = useState<{ [key: string]: string }>({});
|
||||
const [isShareModalOpen, setIsShareModalOpen] = useState(false);
|
||||
const [isFollowing, setIsFollowing] = useState(false);
|
||||
const [isFetchingFollowStatus, setIsFetchingFollowStatus] = useState(true);
|
||||
|
||||
const id = params.id;
|
||||
const currentUser = "%40"+user?.twitter?.username ?? "null"; // Change this to the current user dynamically
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserDetail = async () => {
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://api.callfi.io/get_user_data.php?username=${id}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
const data = await response.json();
|
||||
setUserDetail(data);
|
||||
} catch (error: any) {
|
||||
setError(error.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserDetail();
|
||||
Refresh();
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCalloutHistory = async () => {
|
||||
const fetchCallouts = async () => {
|
||||
let apiUrl = `https://api.callfi.io/get_user_callouts.php?tag=${id}`;
|
||||
if (selectedPeriod === "month") {
|
||||
apiUrl = `https://api.callfi.io/get_user_callouts_monthly.php?tag=${id}`;
|
||||
} else if (selectedPeriod === "week") {
|
||||
apiUrl = `https://api.callfi.io/get_user_callouts_weekly.php?tag=${id}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://api.callfi.io/get_callouts_history.php?username=${id}&period=${weekSelected ? 'week' : "month"}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
const response = await fetch(apiUrl);
|
||||
const data = await response.json();
|
||||
setCallouts(data);
|
||||
} catch (error: any) {
|
||||
setError(error.message);
|
||||
|
||||
// Fetch Dexscreener URLs for each callout
|
||||
const urls: { [key: string]: string } = {};
|
||||
await Promise.all(data.map(async (detail: any) => {
|
||||
try {
|
||||
const response = await axios.get(`https://api.dexscreener.com/latest/dex/tokens/${detail.ca}`);
|
||||
if (response.data && response.data.pairs && response.data.pairs.length > 0) {
|
||||
urls[detail.ca] = response.data.pairs[0].url;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fetching Dexscreener URL for ${detail.ca}:`, error);
|
||||
}
|
||||
}));
|
||||
setDexUrls(urls);
|
||||
} catch (error) {
|
||||
console.error('Error fetching callouts:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCalloutHistory();
|
||||
}, [id]);
|
||||
fetchCallouts();
|
||||
}, [id, selectedPeriod]);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
useEffect(() => {
|
||||
const fetchHistory = async () => {
|
||||
let apiUrl = `https://api.callfi.io/get_callouts_history.php?username=${id}&period=all time`;
|
||||
if (selectedPeriod === "month") {
|
||||
apiUrl = `https://api.callfi.io/get_callouts_history.php?username=${id}&period=month`;
|
||||
} else if (selectedPeriod === "week") {
|
||||
apiUrl = `https://api.callfi.io/get_callouts_history.php?username=${id}&period=week`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl);
|
||||
const data = await response.json();
|
||||
setHistory(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching history:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchHistory();
|
||||
}, [id, selectedPeriod]);
|
||||
|
||||
useEffect(() => {
|
||||
Refresh();
|
||||
}, [currentUser, id]);
|
||||
|
||||
function Refresh(){
|
||||
const fetchFollowStatus = async () => {
|
||||
try {
|
||||
const response = await fetch(`https://api.callfi.io/get_is_following.php?username=${currentUser}&target=${id}`);
|
||||
const data = await response.json();
|
||||
setIsFollowing(data == "1");
|
||||
} catch (error) {
|
||||
console.error('Error fetching follow status:', error);
|
||||
} finally {
|
||||
setIsFetchingFollowStatus(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFollowStatus();
|
||||
const fetchUserData = async () => {
|
||||
try {
|
||||
const response = await fetch(`https://api.callfi.io/get_user_data.php?username=${id}`);
|
||||
const data = await response.json();
|
||||
setUserData(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching user data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserData();
|
||||
}
|
||||
|
||||
const handleFollowClick = async () => {
|
||||
if (!ready || !user) {
|
||||
setLoginAlert("You need to be logged in to follow.");
|
||||
setTimeout(() => setLoginAlert(null), 3000);
|
||||
} else {
|
||||
try {
|
||||
const response = await fetch(`https://api.callfi.io/${isFollowing ? "unfollow" : "follow"}_user.php?username=${currentUser}&target=${id}`);
|
||||
const data = await response.json();
|
||||
Refresh();
|
||||
} catch (error) {
|
||||
console.error('Error following user:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!userData) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const truncateAddress = (address: string) => {
|
||||
return `${address.slice(0, 6)}...${address.slice(-5)}`;
|
||||
};
|
||||
|
||||
const handleShareClick = () => {
|
||||
setIsShareModalOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
<>
|
||||
|
||||
<NextSeo title={`CallFi - ${userData.tag}'s profile`} />
|
||||
<main className='flex flex-col min-h-screen text-white'>
|
||||
<section className='glassmorphism'>
|
||||
<div className='grid grid-cols-3 gap-4 flex w-full flex-row p-3'>
|
||||
|
||||
|
||||
<Link href="/">
|
||||
<button className='mr-3'>
|
||||
<FaArrowLeft />
|
||||
</button>
|
||||
</Link>
|
||||
<h1 className='text-center'>CallFi Profile</h1>
|
||||
</div>
|
||||
|
||||
<div className='rounded-3xl'>
|
||||
<div className='grid grid-cols-3 flex-row p-2'>
|
||||
<img
|
||||
src={userDetail?.img_url}
|
||||
alt={`${userDetail?.tag}'s profile`}
|
||||
className="rounded-full w-full sm:w-36 p-5"
|
||||
src={userData.img_url}
|
||||
className="rounded-full w-full w-36 p-5"
|
||||
/>
|
||||
<div className='flex flex-col p-6 px-2 col-span'>
|
||||
<div className='flex flex-col p-6 px-2'>
|
||||
<div>
|
||||
<p className='font-bold mb-2'>{userDetail?.tag}</p>
|
||||
<p className='text-sm'>500x Gains</p>
|
||||
<p className='text-sm'>50 Points</p>
|
||||
<p className='text-sm'>10 Followers</p>
|
||||
<p className='font-bold mb-2'>{userData.tag}</p>
|
||||
<p className='text-sm'>{userData.gains.toFixed(2)}x Gains</p>
|
||||
<p className='text-sm'>{(userData.points * 100).toFixed(2)} Points</p>
|
||||
<p className='text-sm'>{userData.followerCount} Followers</p>
|
||||
</div>
|
||||
<div className={expandedInfo ? "" : "hidden"}>
|
||||
<p className='text-sm'>150 Callouts</p>
|
||||
<p className='text-sm'>75% Accuracy</p>
|
||||
<p className='text-sm'>200 Days</p>
|
||||
<p className='text-sm'>{userData.calloutCount} Callouts</p>
|
||||
<p className='text-sm'>{parseFloat(userData.accuracy).toFixed(1)}% Accuracy</p>
|
||||
<p className='text-sm'>{userData.daysSinceJoined} Days</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className='flex col-span-1 justify-end m-3 mb-6 items-end'>
|
||||
{expandedInfo ? (<button onClick={() => { setExpandedInfo(false) }}><FaArrowUp /></button>) : (<button onClick={() => { setExpandedInfo(true) }}><FaArrowDown /></button>)}
|
||||
{expandedInfo ? (
|
||||
<button onClick={() => { setExpandedInfo(false) }}><FaArrowUp /></button>
|
||||
) : (
|
||||
<button onClick={() => { setExpandedInfo(true) }}><FaArrowDown /></button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='m-2 grid grid-cols-5 gap-2 px-2'>
|
||||
<button className='glassmorphism p-1 col-span-4 rounded-xl hover:bg-blue-900'>Follow</button>
|
||||
<button className='glassmorphism p-1 flex items-center justify-center rounded-xl hover:bg-blue-900'><FaShare /></button>
|
||||
<button
|
||||
onClick={currentUser == id ? ()=>{} : handleFollowClick}
|
||||
className={`glassmorphism p-1 col-span-4 rounded-xl ${isFollowing ? "hover:bg-red-900" : "hover:bg-blue-900"}`}
|
||||
disabled={currentUser === id}
|
||||
>
|
||||
{currentUser == id ? 'My Account' : (isFollowing ? 'Unfollow' : 'Follow')}
|
||||
</button>
|
||||
<button onClick={handleShareClick} className='glassmorphism p-1 flex items-center justify-center rounded-xl hover:bg-blue-900'><FaShare /></button>
|
||||
</div>
|
||||
{loginAlert && <p className="text-red-500 text-center">{loginAlert}</p>}
|
||||
<div className='grid grid-cols-2 mt-4'>
|
||||
<button className={`justify-center text-center ${activeSelected ? "text-blue-300 bg-opacity-20 bg-black" : " text-sm text-gray-300"}`} onClick={() => { setActiveSelected(true) }}>
|
||||
<p>Active</p>
|
||||
|
|
@ -144,55 +212,19 @@ export default function UserPage({ params }: { params: { id: string } }) {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
{/* Header Ends */}
|
||||
|
||||
|
||||
<section className={`flex w-full ${activeSelected ? "" : "hidden"}`}>
|
||||
<section className="flex flex-col w-full m-2">
|
||||
<div className='flex justify-center p-4'>
|
||||
<div className='glassmorphism rounded-full flex p-1'>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${allTimeSelected ? "glassmorphism-dark" : ""}`} onClick={()=>{SetAllTimeSelected(true)}}>
|
||||
<div className='glassmorphism rounded-full flex p-1 text-sm'>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${selectedPeriod === "all time" ? "glassmorphism-dark" : ""}`} onClick={() => { setSelectedPeriod("all time") }}>
|
||||
All Time
|
||||
</button>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${!allTimeSelected ? "glassmorphism-dark" : ""}`} onClick={()=>{SetAllTimeSelected(false)}}>
|
||||
Timed
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<div className={`${activeSelected ? "" : "hidden"}`}>
|
||||
<div className={`glassmorphism rounded-full m-2 grid grid-cols-4 h-20`}>
|
||||
<img
|
||||
src="https://s2.coinmarketcap.com/static/img/coins/64x64/12870.png"
|
||||
className="rounded-full m-2 flex"
|
||||
/>
|
||||
<div className='grid grid-rows-2'>
|
||||
<div className='items-end justify-start text-center flex text-lg'>
|
||||
COC
|
||||
</div>
|
||||
<p className='text-xs items-center flex justify-start'>
|
||||
0x22...e3caf
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className=' grid grid-rows-2 col-span-2 justify-end mx-5'>
|
||||
<p className='items-end flex'>{"$19.15 => $100"}</p>
|
||||
<p className='justify-end flex'>{"100%"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className={`flex flex-1 w-full ${!activeSelected ? "" : "hidden"}`}>
|
||||
<section className="flex flex-col w-full m-2">
|
||||
<div className='flex justify-center p-4'>
|
||||
<div className='glassmorphism rounded-full w-min flex p-1'>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${weekSelected ? "glassmorphism-dark" : ""}`} onClick={()=>{SetWeekSelected(true)}}>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${selectedPeriod === "week" ? "glassmorphism-dark" : ""}`} onClick={() => { setSelectedPeriod("week") }}>
|
||||
Weekly
|
||||
</button>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${!weekSelected ? "glassmorphism-dark" : ""}`} onClick={()=>{SetWeekSelected(false)}}>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${selectedPeriod === "month" ? "glassmorphism-dark" : ""}`} onClick={() => { setSelectedPeriod("month") }}>
|
||||
Monthly
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -200,66 +232,67 @@ export default function UserPage({ params }: { params: { id: string } }) {
|
|||
</section>
|
||||
</section>
|
||||
|
||||
<div className={`${activeSelected ? "" : "hidden"} flex flex-col justify-center items-center`}>
|
||||
{callouts.map((callout) => (
|
||||
<CalloutCard
|
||||
key={callout.ca}
|
||||
iconUrl={callout.icon_url}
|
||||
token={callout.token}
|
||||
ca={callout.ca}
|
||||
priceAtCreation={callout.price_at_creation}
|
||||
priceNow={callout.price_now}
|
||||
gains={parseFloat(callout.gains)}
|
||||
dexUrl={dexUrls[callout.ca]} // Pass the Dexscreener URL to the CalloutCard
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<section className={`flex w-full ${!activeSelected ? "" : "hidden"}`}>
|
||||
<section className="flex flex-col w-full m-2">
|
||||
<div className='flex justify-center p-4'>
|
||||
<div className='glassmorphism rounded-full w-min flex p-1 text-sm'>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${selectedPeriod !== "month" ? "glassmorphism-dark" : ""}`} onClick={() => { setSelectedPeriod("week") }}>
|
||||
Weekly
|
||||
</button>
|
||||
<button className={`glassmorphism m-1 mx-1 p-1 px-5 rounded-full ${selectedPeriod === "month" ? "glassmorphism-dark" : ""}`} onClick={() => { setSelectedPeriod("month") }}>
|
||||
Monthly
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div className={`${!activeSelected ? "" : "hidden"} flex flex-col justify-center items-center`}>
|
||||
{history.map((entry) => (
|
||||
<CalloutCard
|
||||
key={entry.ca}
|
||||
iconUrl={entry.icon_url}
|
||||
token={entry.id}
|
||||
ca={entry.ca}
|
||||
priceAtCreation={entry.price_at_creation}
|
||||
priceNow={entry.froze_price}
|
||||
gains={parseFloat(entry.gains)}
|
||||
dexUrl={dexUrls[entry.ca]} // Pass the Dexscreener URL to the CalloutCard
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className='flex-1'></div>
|
||||
<Footer />
|
||||
|
||||
</main>
|
||||
<ShareModal
|
||||
isOpen={isShareModalOpen}
|
||||
onClose={() => setIsShareModalOpen(false)}
|
||||
shareLink={window.location.href} // Pass the current URL to the ShareModal
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <main className="flex flex-col min-h-screen text-white">
|
||||
// <Header />
|
||||
// <section className="flex flex-1 w-full p-4">
|
||||
// {loading ? (
|
||||
// <p>Loading...</p>
|
||||
// ) : error ? (
|
||||
// <p>Error: {error}</p>
|
||||
// ) : userDetail ? (
|
||||
// <div>
|
||||
// <div className="user-detail-pane py-10 px-20 glassmorphism rounded-lg flex flex-col items-center md:items-start md:mr-4">
|
||||
// <img
|
||||
// src={userDetail.img_url}
|
||||
// alt={`${userDetail.tag}'s profile`}
|
||||
// className="w-24 h-24 rounded-full mb-10"
|
||||
// />
|
||||
// <h1 className="text-2xl font-bold mb-2">{userDetail.tag}</h1>
|
||||
// <p className="mb-2">Joined: {formatDate(userDetail.joined_date)}</p>
|
||||
// <p className="mb-2">Callout count: {userDetail.calloutCount}</p>
|
||||
// </div>
|
||||
// <div className="content flex-1 p-4">
|
||||
// <div className="tabs flex items-center space-x-4 mb-4 justify-center">
|
||||
// {periods.map((period) => (
|
||||
// <button
|
||||
// key={period}
|
||||
// className={`px-4 py-2 rounded ${selectedPeriod === period ? 'bg-black text-white glassmorphism-dark' : 'bg-gray-700 text-gray-400 glassmorphism'}`}
|
||||
// onClick={() => setSelectedPeriod(period)}
|
||||
// >
|
||||
// {period}
|
||||
// </button>
|
||||
// ))}
|
||||
// </div>
|
||||
// {callouts.map((callout) => (
|
||||
// <div key={callout.ca} className="leaderboard-card glassmorphism mx-auto mb-4 p-6 w-full md:w-2/3 lg:w-1/2">
|
||||
// <div className="flex items-center space-x-4">
|
||||
// <img src={callout.icon_url} alt="Coin Icon" className="w-16 h-16 rounded-full" />
|
||||
// <div className="flex-grow">
|
||||
// <p className="font-bold text-lg">{callout.id}</p>
|
||||
// <p>{`$${callout.price_at_creation} => $${callout.froze_price}`}</p>
|
||||
// </div>
|
||||
// <div className="flex items-center">
|
||||
// <p className="text-right">{`${callout.gains}%`}</p>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
// </div>
|
||||
// ) : (
|
||||
// <p>User not found</p>
|
||||
// )}
|
||||
// </section>
|
||||
// <Footer />
|
||||
// </main>
|
||||
// );
|
||||
}
|
||||
|
||||
export default function WrappedUserPage(props) {
|
||||
return (
|
||||
<PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || 'clxc7nmy906ifhis11rkovoto'}>
|
||||
<UserPage {...props} />
|
||||
</PrivyProvider>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import { motion } from 'framer-motion';
|
|||
import Link from 'next/link';
|
||||
import Header from '@/components/Header';
|
||||
import Footer from '@/components/Footer';
|
||||
import { PrivyProvider } from '@privy-io/react-auth';
|
||||
|
||||
interface User {
|
||||
username: string;
|
||||
|
|
@ -82,4 +83,10 @@ const Page: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
export default function WrappedUsersPage(props) {
|
||||
return (
|
||||
<PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || 'clxc7nmy906ifhis11rkovoto'}>
|
||||
<Page {...props} />
|
||||
</PrivyProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
49
components/CalloutCard.tsx
Normal file
49
components/CalloutCard.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import React from 'react';
|
||||
|
||||
interface CalloutCardProps {
|
||||
iconUrl: string;
|
||||
token: string;
|
||||
ca: string;
|
||||
priceAtCreation: number;
|
||||
priceNow: number;
|
||||
gains: number;
|
||||
dexUrl: string;
|
||||
}
|
||||
|
||||
const CalloutCard: React.FC<CalloutCardProps> = ({
|
||||
iconUrl,
|
||||
token,
|
||||
ca,
|
||||
priceAtCreation,
|
||||
priceNow,
|
||||
gains,
|
||||
dexUrl
|
||||
}) => {
|
||||
const truncateAddress = (address: string) => {
|
||||
return `${address.slice(0, 6)}...${address.slice(-5)}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<a href={dexUrl} target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block">
|
||||
<div className={`glassmorphism rounded-full m-2 grid grid-cols-4 h-20 max-w-96 flex justify-center`}>
|
||||
<img src={iconUrl} className="rounded-full m-2 flex h-16 w-16" />
|
||||
<div className='grid grid-rows-2'>
|
||||
<div className='items-end justify-start text-center flex font-bold'>
|
||||
{token}
|
||||
</div>
|
||||
<p className='text-xs items-start mt-1 flex justify-start ml-1'>
|
||||
{truncateAddress(ca)}
|
||||
</p>
|
||||
</div>
|
||||
<div className='grid grid-rows-2 col-span-2 justify-end mx-5'>
|
||||
<p className='items-end flex text-xs text-nowrap mb-1'>{`$${priceAtCreation} => $${priceNow}`}</p>
|
||||
<p className={`justify-end flex 00 font-bold ${gains > 0 ? 'text-green-700' : 'text-red-700'}`}>{`${(gains * 100).toFixed(2)}%`}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default CalloutCard;
|
||||
|
|
@ -44,11 +44,8 @@ const Header: React.FC = () => {
|
|||
return (
|
||||
<header className="w-full flex justify-between items-center p-4 bg-gray-800 text-white glassmorphism">
|
||||
<div className="flex items-center">
|
||||
<h1 className="text-xl font-bold px-10">CallFi</h1>
|
||||
<Link href="/"><h1 className="text-xl font-bold px-10">CallFi</h1></Link>
|
||||
<nav className="flex space-x-4">
|
||||
<Link href="/">
|
||||
<p className={` text-white hover:underline`}>Dashboard</p>
|
||||
</Link>
|
||||
<Link href="/users">
|
||||
<p className={` text-white hover:underline`}>User Ranking</p>
|
||||
</Link>
|
||||
|
|
|
|||
60
components/ShareModal.tsx
Normal file
60
components/ShareModal.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// components/ShareModal.tsx
|
||||
import { FacebookIcon, FacebookShareButton, RedditIcon, RedditShareButton, TelegramIcon, TelegramShareButton, TwitterIcon, TwitterShareButton } from 'next-share';
|
||||
import React from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
|
||||
const ShareModal = ({ isOpen, onClose, shareLink }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const handleCopyLink = () => {
|
||||
navigator.clipboard.writeText(shareLink);
|
||||
//alert('Link copied to clipboard!');
|
||||
};
|
||||
|
||||
const shareIconSize =48;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||
<div className="glassmorphism p-5 rounded-lg max-w-md w-96 m-2">
|
||||
<div className="flex justify-end text-white">
|
||||
<button onClick={onClose}>
|
||||
<FaTimes />
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 text-center">
|
||||
<h2 className="text-lg font-bold text-white">Share Profile</h2>
|
||||
<div className='flex grid grid-cols-4 ml-5 mt-7 mb-5'>
|
||||
<FacebookShareButton url={shareLink}>
|
||||
<FacebookIcon size={shareIconSize} round />
|
||||
</FacebookShareButton>
|
||||
<TwitterShareButton url={shareLink} >
|
||||
<TwitterIcon size={shareIconSize} round/>
|
||||
</TwitterShareButton>
|
||||
<RedditShareButton url={shareLink} >
|
||||
<RedditIcon size={shareIconSize} round/>
|
||||
</RedditShareButton>
|
||||
<TelegramShareButton url={shareLink} >
|
||||
<TelegramIcon size={shareIconSize} round/>
|
||||
</TelegramShareButton>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={shareLink}
|
||||
className="w-full mt-2 p-2 border border-gray-300 rounded glassmorphism text-white"
|
||||
/>
|
||||
<button
|
||||
onClick={handleCopyLink}
|
||||
className="mt-4 bg-blue-500 glassmorphism text-white py-2 px-4 rounded hover:bg-blue-700"
|
||||
>
|
||||
Copy Link
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShareModal;
|
||||
Loading…
Reference in New Issue
Block a user