Skip to content
Open
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.53.0",
"@tabler/icons-react": "^3.34.1",
"@tanstack/react-query": "^5.84.1",
"@tanstack/react-query": "^5.85.3",
"@tsparticles/engine": "^3.8.1",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.8.1",
Expand Down
7 changes: 5 additions & 2 deletions src/components/home/sections/members.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ export function Members() {
const [selectedMember, setSelectedMember] = useState<Member | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);

const { data: members, isLoading } = useQuery({
const { data: members, isLoading } = useQuery<Member[]>({
queryKey: ['members'],
queryFn: async () => {
return await getMembers()
}
});

// Garantizar que `membersList` siempre sea un arreglo para evitar errores al usar .filter/.map
const membersList: Member[] = Array.isArray(members) ? members : [];

const handleMemberClick = (member: Member) => {
setSelectedMember(member);
setIsModalOpen(true);
Expand Down Expand Up @@ -49,7 +52,7 @@ export function Members() {
<p className="text-gray-500">Cargando miembros...</p>
</div>
)}
{!isLoading && members.filter(member => member.active).map((member) => (
{!isLoading && membersList.filter(member => member.active).map((member) => (
<MemberCard
key={member._id}
member={member}
Expand Down
7 changes: 5 additions & 2 deletions src/components/members/inactive-members.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { InactiveMembersTimeline } from "./sections/timeline";

const InactiveMembers = () => {

const { data: members, isLoading } = useQuery({
const { data: members, isLoading } = useQuery<Member[]>({
queryKey: ['members'],
queryFn: async () => {
const members : Member[] = await getMembers();
Expand All @@ -17,9 +17,12 @@ const InactiveMembers = () => {
}
});

// Asegurar que `membersList` sea siempre un arreglo
const membersList: Member[] = Array.isArray(members) ? members : [];


// Group and sort members by period
const grouped = isLoading ? {} as Record<string, Member[]> : members.reduce(
const grouped = isLoading ? {} as Record<string, Member[]> : membersList.reduce(
(acc, m) => {
const period = m.memberSince || "Sin periodo";
(acc[period] = acc[period] || []).push(m);
Expand Down
131 changes: 105 additions & 26 deletions src/components/shared/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,123 @@
'use client';

"use client";
import { useEffect, useState } from "react";
import AnimatedTooltip from "./ui/tooltip";
import { IconBrandInstagram, IconBrandLinkedin } from "@tabler/icons-react";
import {
getGitHubContributorsFromRepos,
GitHubContributor,
} from "@/controllers/github.controller";

export default function Footer() {
const [contributors, setContributors] = useState<GitHubContributor[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
setIsMounted(true);
}, []);

useEffect(() => {
if (!isMounted) return;

const fetchContributors = async () => {
try {
setIsLoading(true);

// Obtener contribuidores
console.log("Cargando contribuidores del proyecto...");
const contributorsData = await getGitHubContributorsFromRepos(
undefined,
6,
10
);
setContributors(contributorsData);
} catch (error) {
console.error("Error loading contributors:", error);
} finally {
setIsLoading(false);
}
};

fetchContributors();
}, [isMounted]);

// Convertir contribuidores al formato del tooltip
const contributorItems = contributors.map((contributor) => ({
id: contributor.id,
name: contributor.login,
designation: "ACM Member",
image: contributor.avatar_url,
html_url: contributor.html_url,
className: "border-gray-200 hover:border-blue-400",
}));

// Fallback al logo ACM si no hay contribuidores
const acmLogo = [
{
id: 1,
name: "ACM Javeriana",
designation: "Capítulo Universitario",
image: "/Logo_Oscuro.svg",
imageDark: "/Logo_Claro.svg",
className: "border-transparent",
},
];

const displayItems =
!isMounted || isLoading || contributorItems.length === 0
? acmLogo
: contributorItems;

return (
<footer className="w-full px-6 py-8 bg-white/80 dark:bg-gray-900/80 backdrop-blur-sm shadow-lg border-t border-gray-200/20 dark:border-gray-700/20 mt-auto">
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-3 items-center">
{/* Título a la izquierda */}
<div className="flex flex-col justify-self-start">
<h3 className="text-sm text-center text-[--azul-noche] dark:text-white">Capítulo Javeriano ACM</h3>
{/*
Responsive
*/}
<div className="grid grid-cols-1 md:grid-cols-3 items-center gap-4 md:gap-0 text-center md:text-left">
{/* Información izquierda */}
<div className="flex items-center justify-center md:justify-start whitespace-nowrap">
<span className="font-montserrat text-lg md:text-lg text-[--azul-noche] dark:text-white">
Capítulo Javeriano
</span>
<span className="ml-2 md:text-xl font-semibold text-[--azul-noche] dark:text-white" style={{ fontFamily: "'bc-liguria', sanserif" }}>
ACM
</span>
</div>

{/* Logo en el centro */}
<div className="flex justify-center">
<img
src="/Logo_Oscuro.svg"
alt="Logo ACM Javeriana"
className="h-8 dark:hidden"
draggable={false}
/>
<img
src="/Logo_Claro.svg"
alt="Logo ACM Javeriana"
className="h-8 hidden dark:block"
draggable={false}
{/* Contribuidores del proyecto en el centro*/}
<div className="flex justify-center relative z-0">
<AnimatedTooltip
items={displayItems}
className="relative z-0"
tooltipOffset="-translate-x-3/4"
/>
</div>

{/* Redes sociales a la derecha - apiladas verticalmente */}
<div className="flex flex-col items-end gap-2 justify-self-end">
<a href="https://www.linkedin.com/company/capitulo-javeriano-acm/" target="_blank" rel="noopener noreferrer">
<IconBrandLinkedin className="w-5 h-5 text-[--azul-noche] dark:text-white hover:opacity-70 transition-opacity" />
{/* Redes sociales a la derecha*/}
<div className="flex items-center justify-center md:justify-end gap-3 md:gap-4">
<a
href="https://www.linkedin.com/company/capitulo-javeriano-acm/"
target="_blank"
rel="noopener noreferrer"
aria-label="LinkedIn"
className="inline-flex"
>
<IconBrandLinkedin className="w-7 h-7 md:w-8 md:h-8 text-[--azul-noche] dark:text-white hover:opacity-70 transition-opacity" />
</a>
<a href="https://www.instagram.com/acmjaveriana" target="_blank" rel="noopener noreferrer">
<IconBrandInstagram className="w-5 h-5 text-[--azul-noche] dark:text-white hover:opacity-70 transition-opacity" />

<a
href="https://www.instagram.com/acmjaveriana?igsh=N3VjZGw0OHE3eG1x"
target="_blank"
rel="noopener noreferrer"
aria-label="Instagram"
className="inline-flex"
>
<IconBrandInstagram className="w-7 h-7 md:w-8 md:h-8 text-[--azul-noche] dark:text-white hover:opacity-70 transition-opacity" />
</a>
</div>
</div>
</div>
</footer>
);
}
}
7 changes: 7 additions & 0 deletions src/components/shared/ui/cn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type ClassValue } from "clsx";
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Loading