Skip to content
Open
Show file tree
Hide file tree
Changes from 10 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
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