Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion components/Profile.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { FaGithub, FaLinkedin } from "react-icons/fa";
import { FaGithub, FaLinkedin, FaHeart } from "react-icons/fa";
import { FaXTwitter, FaLocationDot } from "react-icons/fa6";

function Profile({ data }) {
Expand All @@ -9,6 +9,28 @@ function Profile({ data }) {
function Card({ data }) {
const cardRef = React.useRef();
const [imageSrc, setImageSrc] = useState(data.avatar);
const [likes, setLikes] = useState(0);
const [liked, setLiked] = useState(false);

// localStorage key for this profile — prefer unique id if available
const profileKey = `profile:${data.id || data.name}`;

useEffect(() => {
try {
const raw = localStorage.getItem('profileLikes');
const map = raw ? JSON.parse(raw) : {};
const entry = map[profileKey];
if (entry) {
setLikes(Number(entry.count) || 0);
setLiked(Boolean(entry.liked));
} else {
setLikes(0);
setLiked(false);
}
} catch (e) {
// ignore parse errors
}
}, [profileKey]);

useEffect(() => {
setImageSrc(data.avatar);
Expand Down Expand Up @@ -105,6 +127,38 @@ function Card({ data }) {
<a href={data.social.LinkedIn} target="_blank" rel="noreferrer">
<FaLinkedin className="text-2xl text-blue-600 duration-300 hover:scale-125" />
</a>
{/* Like button */}
<button
onClick={() => {
// toggle like
const willLike = !liked;
const newCount = willLike ? likes + 1 : Math.max(0, likes - 1);
setLikes(newCount);
setLiked(willLike);
try {
const raw = localStorage.getItem('profileLikes');
const map = raw ? JSON.parse(raw) : {};
map[profileKey] = { count: newCount, liked: willLike };
localStorage.setItem('profileLikes', JSON.stringify(map));
// notify other parts of the app that likes changed
try {
window.dispatchEvent(new Event('profileLikesChanged'));
} catch (e) {
// ignore if window not available
}
} catch (e) {
// ignore storage errors
}
}}
aria-pressed={liked}
aria-label={liked ? 'Unlike profile' : 'Like profile'}
className={`flex items-center gap-2 text-2xl transition-all duration-200 ${
liked ? 'text-red-500' : 'text-gray-400 hover:text-red-400'
}`}
>
<FaHeart />
<span className="text-sm font-medium">{likes}</span>
</button>
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions components/ProfileList.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[
"priyanshuSingh.json",
"StarKnightt.json",
"prashantrai-30.json",
"vsnikhilvs.json",
Expand Down
22 changes: 21 additions & 1 deletion components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import useTheme from "./hooks/useTheme";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCode, faMoon, faSun } from "@fortawesome/free-solid-svg-icons";

function Sidebar() {
function Sidebar({ topProfiles = [] }) {
const [mount, theme, toggleTheme] = useTheme();

return (
Expand Down Expand Up @@ -44,6 +44,26 @@ function Sidebar() {
</button>
</a>
</div>
{/* Top ranking section (client-side likes) */}
{topProfiles && topProfiles.length > 0 && (
<div className="mt-6">
<h4 className="mb-2 text-sm font-semibold dark:text-white">Top Contributors</h4>
<ul className="space-y-2">
{topProfiles.map((p, idx) => (
<li key={p.id || p.name} className="flex items-center gap-3">
<div className="h-8 w-8 flex-shrink-0 overflow-hidden rounded-full">
<img src={p.avatar} alt={p.name} className="h-full w-full object-cover" />
</div>
<div className="flex-1 text-sm">
<div className="font-medium dark:text-white">{p.name}</div>
<div className="text-xs text-textSecondary">{p._likes || 0} likes</div>
</div>
<div className="text-sm text-textSecondary">#{idx + 1}</div>
</li>
))}
</ul>
</div>
)}
</div>
);
}
Expand Down
54 changes: 51 additions & 3 deletions pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function App() {
const [currentPage, setCurrentPage] = useState(1);
const [shuffledProfiles, setShuffledProfiles] = useState([]);
const [loadingProfiles, setLoadingProfiles] = useState(false);
const [topProfiles, setTopProfiles] = useState([]);
const recordsPerPage = 20;

const router = useRouter();
Expand All @@ -41,8 +42,25 @@ function App() {
const combinedData = await Promise.all(promises).then((results) =>
results.flat()
);
setCombinedData(combinedData);
setShuffledProfiles(shuffleProfiles(combinedData));
setCombinedData(combinedData);
setShuffledProfiles(shuffleProfiles(combinedData));
// compute top profiles from localStorage likes (client-only)
if (typeof window !== "undefined") {
try {
const raw = localStorage.getItem("profileLikes");
const map = raw ? JSON.parse(raw) : {};
const withLikes = combinedData.map((p) => {
const key = `profile:${p.id || p.name}`;
const count = map[key] && map[key].count ? Number(map[key].count) : 0;
return { ...p, _likes: count };
});
const top = withLikes.sort((a, b) => b._likes - a._likes).slice(0, 5);
setTopProfiles(top);
} catch (e) {
// ignore parsing errors
setTopProfiles([]);
}
}
} catch (error) {
console.error("Error combining data:", error);
setCombinedData([]);
Expand All @@ -52,6 +70,36 @@ function App() {
};

combineData();
// recompute topProfiles helper
const computeTopProfiles = () => {
if (typeof window === "undefined") return;
try {
const raw = localStorage.getItem("profileLikes");
const map = raw ? JSON.parse(raw) : {};
const withLikes = combinedData.map((p) => {
const key = `profile:${p.id || p.name}`;
const count = map[key] && map[key].count ? Number(map[key].count) : 0;
return { ...p, _likes: count };
});
const top = withLikes.sort((a, b) => b._likes - a._likes).slice(0, 5);
setTopProfiles(top);
} catch (e) {
setTopProfiles([]);
}
};

// update when other tabs change localStorage
const storageHandler = (e) => {
if (e.key === 'profileLikes') computeTopProfiles();
};
const customHandler = () => computeTopProfiles();
window.addEventListener('storage', storageHandler);
window.addEventListener('profileLikesChanged', customHandler);

return () => {
window.removeEventListener('storage', storageHandler);
window.removeEventListener('profileLikesChanged', customHandler);
};
}, []);

const shuffleProfiles = (array) => {
Expand Down Expand Up @@ -139,7 +187,7 @@ function App() {

return currentUrl === "/" ? (
<div className="App flex flex-col bg-primaryColor dark:bg-secondaryColor md:flex-row">
<Sidebar />
<Sidebar topProfiles={topProfiles} />
<div
className="w-full pl-5 pr-4 md:h-screen md:w-[77%] md:overflow-y-scroll md:py-7"
ref={profilesRef}
Expand Down
11 changes: 11 additions & 0 deletions public/data/priyanshuSingh.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "Priyanshu Singh",
"location": "Greater Noida, India",
"bio": "Freelance Web Developer | Tech Enthusiast | Open Source Contributor | Lifelong Learner",
"avatar": "https://avatars.githubusercontent.com/u/198406931?v=4",
"skills": ["HTML", "CSS", "Javascript", "Django", "Python", "Git"],
"social": {
"GitHub": "https://github.com/Priyanshu2004-Singh",
"LinkedIn": "https://www.linkedin.com/in/priyanshu-singh-a17807231/"
}
}