Skip to content

Commit 3a23ce1

Browse files
authored
Merge pull request #44 from FSDSTR0225/feature/notificaciones
Feature/notificaciones
2 parents d13c7ae + 2b8cb05 commit 3a23ce1

File tree

5 files changed

+140
-102
lines changed

5 files changed

+140
-102
lines changed

src/components/home/NumbersSection.jsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const NumbersSection = () => {
88
const [devs, setDevs] = useState([]);
99
const [companies, setCompanies] = useState([]);
1010

11-
useEffect(() => {
11+
useEffect(() => {
1212
const fetchOffers = async () => {
1313
try {
1414
const offersData = await getOffers();
@@ -42,35 +42,35 @@ export const NumbersSection = () => {
4242
}, []);
4343

4444
let acceptedApplications = 0;
45-
offers.map(offer => {
46-
if (offer.applicants) {
47-
offer.applicants.map(applicant => {
48-
if (applicant.status === 'accepted') {
49-
acceptedApplications++;
50-
}
51-
});
52-
}
53-
});
45+
offers.map(offer => {
46+
if (offer.applicants) {
47+
offer.applicants.map(applicant => {
48+
if (applicant.status === 'accepted') {
49+
acceptedApplications++;
50+
}
51+
});
52+
}
53+
});
5454

5555
return (
56-
<div className="flex justify-center items-center w-full mt-8">
57-
<div className="w-full max-w-250 bg-neutral-80 border border-neutral-60 rounded-3xl py-16 px-16">
58-
<div className='grid grid-cols-4 gap-8 text-center'>
56+
<div className="flex justify-center items-center w-full mt-8 px-2">
57+
<div className="w-full max-w-6xl bg-neutral-80 border border-neutral-60 rounded-3xl py-8 px-2 sm:py-12 sm:px-6 md:py-16 md:px-16">
58+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8 text-center">
5959
<div className="flex flex-col items-center">
60-
<span className='text-4xl font-bold text-primary-50 mb-2 block'>+{devs.length * 100}</span>
61-
<span className='text-neutral-20 text-sm block'>Developers</span>
60+
<span className="text-3xl sm:text-4xl md:text-5xl font-bold text-primary-50 mb-2 block">+{devs.length * 100}</span>
61+
<span className="text-neutral-20 text-sm md:text-base block">Developers</span>
6262
</div>
6363
<div className="flex flex-col items-center">
64-
<span className='text-4xl font-bold text-secondary-50 mb-2 block'>+{companies.length * 100}</span>
65-
<span className='text-neutral-20 text-sm block'>Companies</span>
64+
<span className="text-3xl sm:text-4xl md:text-5xl font-bold text-secondary-50 mb-2 block">+{companies.length * 100}</span>
65+
<span className="text-neutral-20 text-sm md:text-base block">Companies</span>
6666
</div>
6767
<div className="flex flex-col items-center">
68-
<span className='text-4xl font-bold text-primary-50 mb-2 block'>+{offers.length * 100}</span>
69-
<span className='text-neutral-20 text-sm block'>Active Offers</span>
68+
<span className="text-3xl sm:text-4xl md:text-5xl font-bold text-primary-50 mb-2 block">+{offers.length * 100}</span>
69+
<span className="text-neutral-20 text-sm md:text-base block">Active Offers</span>
7070
</div>
7171
<div className="flex flex-col items-center">
72-
<span className='text-4xl font-bold text-secondary-50 mb-2 block'>+{acceptedApplications * 100}</span>
73-
<span className='text-neutral-20 text-sm block'>Applications accepted</span>
72+
<span className="text-3xl sm:text-4xl md:text-5xl font-bold text-secondary-50 mb-2 block">+{acceptedApplications * 100}</span>
73+
<span className="text-neutral-20 text-sm md:text-base block">Applications accepted</span>
7474
</div>
7575
</div>
7676
</div>

src/layout/Header.jsx

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,15 @@ export const Header = () => {
1818
};
1919

2020
const getNotificationText = (notif) => {
21+
if (notif.type === 1) return null;
2122
const types = {
22-
1: `${notif.senderName} te ha enviado un mensaje`,
23-
2: `${notif.senderName} ha dado like a tu publicación`,
23+
2: `${notif.senderName} ha dado like a tu Proyecto`,
2424
3: `${notif.senderName} ha comentado en tu foto`,
2525
// añade más tipos según necesites
2626
};
27-
2827
return types[notif.type] || "Tienes una nueva notificación";
2928
};
3029

31-
3230
return (
3331
<header className='bg-neutral-80 py-2 pl-2 drawer border-b-1 border-neutral-70'>
3432
<input
@@ -45,19 +43,23 @@ export const Header = () => {
4543
{/* Bell icon y menú de notificaciones */}
4644
<div className="mx-4 flex items-center">
4745
<div className="relative">
48-
<button
49-
type="button"
50-
className="relative flex items-center justify-center text-white hover:text-gray-200"
51-
aria-label="Notificaciones"
52-
onClick={() => setNotificationsOpen((prev) => !prev)}
53-
>
54-
<CiBellOn className="h-6 w-6" />
55-
{notifications.length > 0 && (
56-
<span className="absolute -top-1 -right-1 h-4 w-4 bg-red-500 text-white text-xs rounded-full flex items-center justify-center">
57-
{notifications.length}
58-
</span>
59-
)}
60-
</button>
46+
{
47+
profile && (
48+
<button
49+
type="button"
50+
className="relative flex items-center justify-center text-white hover:text-gray-200"
51+
aria-label="Notificaciones"
52+
onClick={() => setNotificationsOpen((prev) => !prev)}
53+
>
54+
<CiBellOn className="h-6 w-6" />
55+
{notifications.filter(notif => notif.type !== 1).length > 0 && (
56+
<span className="absolute -top-1 -right-1 h-4 w-4 bg-red-500 text-white text-xs rounded-full flex items-center justify-center">
57+
{notifications.filter(notif => notif.type !== 1).length}
58+
</span>
59+
)}
60+
</button>
61+
)
62+
}
6163

6264
{/* Panel de notificaciones */}
6365
{notificationsOpen && (
@@ -74,20 +76,22 @@ export const Header = () => {
7476
</div>
7577

7678
<div className="max-h-72 overflow-y-auto p-2 space-y-2">
77-
{notifications.length > 0 ? (
78-
notifications.map((notif, index) => (
79-
<div
80-
key={notif._id || `${notif.senderName}-${notif.type}-${index}`}
81-
className="w-full bg-neutral-60 text-base-content shadow-md rounded-md p-3"
82-
>
83-
<div className="text-sm font-medium">
84-
{getNotificationText(notif)}
79+
{notifications.filter(notif => notif.type !== 1).length > 0 ? (
80+
notifications
81+
.filter(notif => notif.type !== 1)
82+
.map((notif, index) => (
83+
<div
84+
key={notif._id || `${notif.senderName}-${notif.type}-${index}`}
85+
className="w-full bg-neutral-60 text-base-content shadow-md rounded-md p-3"
86+
>
87+
<div className="text-sm font-medium">
88+
{getNotificationText(notif)}
89+
</div>
90+
<span className="text-xs opacity-60 block mt-1">
91+
{formatMessageTime(notif.createdAt)}
92+
</span>
8593
</div>
86-
<span className="text-xs opacity-60 block mt-1">
87-
{formatMessageTime(notif.createdAt)}
88-
</span>
89-
</div>
90-
))
94+
))
9195
) : (
9296
<div className="p-3 text-sm text-gray-400 text-center">
9397
No hay notificaciones
@@ -96,7 +100,6 @@ export const Header = () => {
96100
</div>
97101
</div>
98102
)}
99-
100103
</div>
101104
</div>
102105

src/layout/chat/ChatPanel.jsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ export default function ChatPanel({ onClose, user }) {
114114
setMessages((prevMessages) => [...prevMessages, resp]);
115115
//Emitir evento de socket para enviar la notificación
116116
socket.emit("sendNotification", {
117+
senderId: profile._id, // <--- agrega esto
117118
senderName: SENDER_NAME,
118119
receiverId: selectedUser._id,
119-
receiverName: selectedUser.name, // o el campo correspondiente si tiene otro nombre
120-
type: 1 // Puedes usar un string o número según tu backend
120+
receiverName: selectedUser.name,
121+
type: 1
121122
});
122123

123124
setUsuariosConectados((prev) => {
@@ -168,11 +169,30 @@ export default function ChatPanel({ onClose, user }) {
168169

169170

170171
{screen === "welcome" && (
171-
<WelcomeScreen users={usuariosConectados} handleSelectedUser={handleSelectedUser} onlineUsers={onlineUsers} user={user} onClose={onClose} />
172+
<WelcomeScreen users={usuariosConectados}
173+
handleSelectedUser={handleSelectedUser}
174+
onlineUsers={onlineUsers}
175+
user={user}
176+
notifications={notifications}
177+
profile={profile}
178+
onClose={onClose} />
172179
)}
173180

174181
{screen === "chat" && (
175-
<ChatScreen onClose={onClose} messages={messages} messageEndRef={messageEndRef} userSelected={selectedUser} onlineUsers={onlineUsers} profile={profile} backToWelcome={backToWelcome} sendMessage={handleSendMessage} fileInputRef={fileInputRef} imagePreview={imagePreview} setMessage={setMessage} message={message} removeImage={removeImage} imageChange={handleImageChange} />
182+
<ChatScreen onClose={onClose}
183+
messages={messages}
184+
messageEndRef={messageEndRef}
185+
userSelected={selectedUser}
186+
onlineUsers={onlineUsers}
187+
profile={profile}
188+
backToWelcome={backToWelcome}
189+
sendMessage={handleSendMessage}
190+
fileInputRef={fileInputRef}
191+
imagePreview={imagePreview}
192+
setMessage={setMessage}
193+
message={message}
194+
removeImage={removeImage}
195+
imageChange={handleImageChange} />
176196
)}
177197

178198

src/layout/chat/components/WelcomeScreen.jsx

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import React from 'react'
22
import { AvatarImage } from '../../../components/AvatarImage'
33
import { NameUsers } from '../../../components/NameUsers'
44

5-
export const WelcomeScreen = ({users, onlineUsers, user, handleSelectedUser, onClose}) => {
5+
export const WelcomeScreen = ({ users, onlineUsers, user, handleSelectedUser, onClose, notifications, profile }) => {
6+
console.log("+++++++++++++", notifications)
67
return (
78
<>
89
<div
@@ -19,26 +20,39 @@ export const WelcomeScreen = ({users, onlineUsers, user, handleSelectedUser, onC
1920
</div>
2021

2122
<div className="flex flex-col mt-2 p-2 gap-4 space-x-2 overflow-x-auto bg-neutral-">
22-
{users.map((usuario) => (
23-
<button
24-
key={usuario._id}
25-
className={`btn p-2 py-6 flex items-center justify-between mx-2 focus:outline-none ${
26-
(usuario?.role?.type || usuario?.roles?.type) === "recruiter"
23+
{users.map((usuario) => {
24+
// Cuenta notificaciones de tipo 1 que vienen de este usuario y son para el usuario logueado
25+
const newMessages = notifications.filter(
26+
n =>
27+
n.senderId === usuario._id &&
28+
n.type === 1 &&
29+
n.receiverId === profile?._id
30+
).length;
31+
32+
return (
33+
<button
34+
key={usuario._id}
35+
className={`relative btn p-2 py-6 flex items-center justify-between mx-2 focus:outline-none ${(usuario?.role?.type || usuario?.roles?.type) === "recruiter"
2736
? "bg-primary-50/10 border-primary-50 shadow-primary-50"
2837
: "bg-secondary-50/10 border-secondary-50 hover:border-secondary-300 shadow-secondary-50"
29-
} hover:scale-105 shadow transition-all duration-200 ease-in-out`}
30-
onClick={() => handleSelectedUser(usuario)}
31-
type="button"
32-
>
33-
<div>
34-
<AvatarImage user={usuario} width={8} />
35-
</div>
36-
<NameUsers user={usuario} classProps={"text-xs "} />
37-
<div className="text-sm text-zinc-400">
38-
{onlineUsers.includes(usuario._id) ? "online" : "offline"}
39-
</div>
40-
</button>
41-
))}
38+
} hover:scale-105 shadow transition-all duration-200 ease-in-out`}
39+
onClick={() => handleSelectedUser(usuario)}
40+
type="button"
41+
>
42+
<div>
43+
<AvatarImage user={usuario} width={8} />
44+
</div>
45+
<NameUsers user={usuario} classProps={"text-xs "} />
46+
<div className="text-sm relative">
47+
{newMessages > 0 && (
48+
<span className="bg-red-500 text-white text-xs px-2 py-0.5 rounded-full animate-pulse inline-block ml-2">
49+
{newMessages}
50+
</span>
51+
)}
52+
</div>
53+
</button>
54+
);
55+
})}
4256
</div>
4357
</>
4458
)

src/layout/chat/context/ChatContext.jsx

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,44 @@
1-
import { useState } from 'react';
1+
import { useContext, useState } from 'react';
22
import { createContext } from 'react';
3-
3+
import { AuthContext } from "../../../context/authContext";
44
export const ChatContext = createContext();
5-
export const ChatProvider = ({children}) => {
6-
const [isChatOpen, setIsChatOpen] = useState(false);
7-
const [selectedUser, setSelectedUser] = useState(null);
8-
const [isOpen, setIsOpen] = useState(false);
9-
const [screen, setScreen] = useState("welcome");
10-
11-
const toggleChat = () => {
5+
export const ChatProvider = ({ children }) => {
6+
const [isChatOpen, setIsChatOpen] = useState(false);
7+
const [selectedUser, setSelectedUser] = useState(null);
8+
const [isOpen, setIsOpen] = useState(false);
9+
const [screen, setScreen] = useState("welcome");
10+
const { setNotifications } = useContext(AuthContext);
11+
12+
const toggleChat = () => {
1213
setIsOpen(!isOpen);
1314
};
1415

1516
const backToWelcome = () => {
16-
17-
setScreen("welcome");
18-
setSelectedUser(null);
19-
}
20-
21-
22-
const openChat = (user) => {
23-
toggleChat();
24-
setScreen("chat");
25-
setSelectedUser(user);
26-
setIsChatOpen(true);
27-
};
17+
setScreen("welcome");
18+
setSelectedUser(null);
19+
}
20+
const openChat = (user) => {
21+
toggleChat();
22+
setScreen("chat");
23+
setSelectedUser(user);
24+
setIsChatOpen(true);
25+
};
2826

29-
const closeChat = () => {
30-
setSelectedUser(null);
31-
setIsChatOpen(false);
32-
};
27+
const closeChat = () => {
28+
setSelectedUser(null);
29+
setIsChatOpen(false);
30+
};
3331

34-
const handleSelectedUser = (usuario) => {
32+
const handleSelectedUser = (usuario) => {
33+
setNotifications((prev) =>
34+
prev.filter(n => String(n.senderId) !== String(usuario._id))
35+
);
3536
setScreen("chat");
3637
setSelectedUser(usuario);
3738
}
3839

3940
return (
40-
<ChatContext.Provider value={{ isChatOpen, selectedUser, setSelectedUser, openChat, closeChat, toggleChat, isOpen, backToWelcome, handleSelectedUser, screen}}>
41+
<ChatContext.Provider value={{ isChatOpen, selectedUser, setSelectedUser, openChat, closeChat, toggleChat, isOpen, backToWelcome, handleSelectedUser, screen }}>
4142
{children}
4243
</ChatContext.Provider>
4344
)

0 commit comments

Comments
 (0)