notificacoes
This commit is contained in:
83
src/App.jsx
83
src/App.jsx
@@ -1794,35 +1794,88 @@ export default function App() {
|
|||||||
{showNotificationsModal && (
|
{showNotificationsModal && (
|
||||||
<div className="fixed inset-0 z-[200] flex items-center justify-center bg-black/60 backdrop-blur-sm p-6" onClick={() => setShowNotificationsModal(false)}>
|
<div className="fixed inset-0 z-[200] flex items-center justify-center bg-black/60 backdrop-blur-sm p-6" onClick={() => setShowNotificationsModal(false)}>
|
||||||
<Card className="w-full max-w-md p-8 animate-in zoom-in-95 flex flex-col max-h-[80vh]" darkMode={darkMode} onClick={e => e.stopPropagation()}>
|
<Card className="w-full max-w-md p-8 animate-in zoom-in-95 flex flex-col max-h-[80vh]" darkMode={darkMode} onClick={e => e.stopPropagation()}>
|
||||||
<div className="flex items-center justify-between mb-8">
|
{/* Header */}
|
||||||
<h3 className="text-2xl font-black text-inherit flex items-center gap-3"><Bell size={24} className="text-primary-600" /> {t('notificationsModal')}</h3>
|
<div className="flex items-center justify-between mb-6">
|
||||||
<button onClick={() => setShowNotificationsModal(false)} className="p-2 bg-gray-100 dark:bg-gray-800 rounded-full hover:scale-110 transition-all text-inherit"><X size={20} /></button>
|
<div>
|
||||||
|
<h3 className="text-2xl font-black text-inherit flex items-center gap-3">
|
||||||
|
<Bell size={24} className="text-primary-600" /> {t('notificationsModal')}
|
||||||
|
</h3>
|
||||||
|
{notifications.filter(n => !n.read).length > 0 && (
|
||||||
|
<p className="text-[10px] font-black uppercase tracking-widest text-primary-600 mt-1">
|
||||||
|
{notifications.filter(n => !n.read).length} {language === 'PT' ? 'nova(s)' : language === 'EN' ? 'new' : language === 'ES' ? 'nueva(s)' : language === 'FR' ? 'nouvelle(s)' : 'neue'}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{notifications.filter(n => !n.read).length > 0 && (
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
const batch = writeBatch(db);
|
||||||
|
notifications.filter(n => !n.read).forEach(n => {
|
||||||
|
const ref = doc(db, 'artifacts', appId, 'users', user.uid, 'notifications', n.id);
|
||||||
|
batch.update(ref, { read: true });
|
||||||
|
});
|
||||||
|
await batch.commit();
|
||||||
|
}}
|
||||||
|
className="text-[9px] font-black uppercase tracking-widest text-primary-600 hover:underline px-3 py-2 hover:bg-primary-50 dark:hover:bg-primary-900/20 rounded-xl transition-all"
|
||||||
|
>
|
||||||
|
{t('markAllRead')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button onClick={() => setShowNotificationsModal(false)} className="p-2 bg-gray-100 dark:bg-gray-800 rounded-full hover:scale-110 transition-all text-inherit"><X size={20} /></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-y-auto space-y-4 custom-scrollbar">
|
|
||||||
|
{/* Lista */}
|
||||||
|
<div className="flex-1 overflow-y-auto space-y-3 custom-scrollbar">
|
||||||
{notifications.length === 0 ? (
|
{notifications.length === 0 ? (
|
||||||
<div className="py-12 text-center opacity-30 font-black uppercase tracking-[0.3em] text-sm">{t('noNotifications')}</div>
|
<div className="py-16 text-center flex flex-col items-center gap-4 opacity-30">
|
||||||
|
<Bell size={40} />
|
||||||
|
<span className="font-black uppercase tracking-[0.3em] text-sm">{t('noNotifications')}</span>
|
||||||
|
</div>
|
||||||
) : notifications.map(notif => (
|
) : notifications.map(notif => (
|
||||||
<div key={notif.id} className={`p-4 rounded-2xl flex items-start gap-4 ${!notif.read ? 'bg-primary-50 dark:bg-primary-900/20' : 'bg-gray-50 dark:bg-gray-800'}`}>
|
<div
|
||||||
<div className={`p-3 rounded-full ${!notif.read ? 'bg-primary-100 text-primary-600 dark:bg-primary-900/40 dark:text-primary-400' : 'bg-gray-200 text-gray-500 dark:bg-gray-700'}`}>
|
key={notif.id}
|
||||||
<Bell size={20} />
|
className={`p-4 rounded-2xl flex items-start gap-4 transition-all ${
|
||||||
|
!notif.read
|
||||||
|
? 'bg-primary-50 dark:bg-primary-900/20 border border-primary-100 dark:border-primary-800/40'
|
||||||
|
: (darkMode ? 'bg-gray-800/60' : 'bg-gray-50')
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* Ícone da Notificação */}
|
||||||
|
<div className={`shrink-0 w-12 h-12 flex items-center justify-center rounded-2xl text-xl shadow-sm ${
|
||||||
|
!notif.read
|
||||||
|
? 'bg-primary-100 dark:bg-primary-900/50'
|
||||||
|
: (darkMode ? 'bg-gray-700' : 'bg-gray-200')
|
||||||
|
}`}>
|
||||||
|
{notif.type === 'look_copied' ? '✂️' : <Bell size={20} />}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
|
||||||
<p className="font-bold text-sm text-inherit">
|
{/* Conteúdo */}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="font-bold text-sm leading-snug text-inherit">
|
||||||
{notif.type === 'look_copied' && (
|
{notif.type === 'look_copied' && (
|
||||||
<>O utilizador <span className="text-primary-600">{notif.copiedByEmail}</span> guardou o seu look "{notif.lookName}" {t('inTheirCloset')}</>
|
<>
|
||||||
|
<span className="text-primary-600 font-black">{notif.copiedByEmail}</span>
|
||||||
|
{' '}{t('lookCopiedBy')}{' '}
|
||||||
|
<span className="italic">"{notif.lookName}"</span>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-[10px] uppercase font-black tracking-widest opacity-40 mt-2">
|
<p className="text-[10px] uppercase font-black tracking-widest opacity-40 mt-1.5">
|
||||||
{new Date(notif.createdAt).toLocaleDateString()} às {new Date(notif.createdAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
|
{new Date(notif.createdAt).toLocaleDateString()} às {new Date(notif.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Botão marcar como lida */}
|
||||||
{!notif.read && (
|
{!notif.read && (
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'notifications', notif.id);
|
const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'notifications', notif.id);
|
||||||
await updateDoc(docRef, { read: true });
|
await updateDoc(docRef, { read: true });
|
||||||
}}
|
}}
|
||||||
className="p-2 text-primary-600 hover:bg-primary-100 dark:hover:bg-primary-900/40 rounded-xl"
|
className="shrink-0 p-2 text-primary-600 hover:bg-primary-100 dark:hover:bg-primary-900/40 rounded-xl transition-all"
|
||||||
|
title="Marcar como lida"
|
||||||
>
|
>
|
||||||
<Check size={16} />
|
<Check size={16} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -149,6 +149,8 @@ export const translations = {
|
|||||||
sendMessage: "Enviar Mensagem",
|
sendMessage: "Enviar Mensagem",
|
||||||
notificationsModal: "Notificações",
|
notificationsModal: "Notificações",
|
||||||
noNotifications: "Sem Notificações",
|
noNotifications: "Sem Notificações",
|
||||||
|
markAllRead: "Marcar todas como lidas",
|
||||||
|
lookCopiedBy: "copiou o seu look",
|
||||||
userSavedLook: "guardou o seu look",
|
userSavedLook: "guardou o seu look",
|
||||||
inTheirCloset: "no armário dele!",
|
inTheirCloset: "no armário dele!",
|
||||||
sharedLookTitle: "Look Partilhado",
|
sharedLookTitle: "Look Partilhado",
|
||||||
@@ -321,6 +323,8 @@ export const translations = {
|
|||||||
sendMessage: "Send Message",
|
sendMessage: "Send Message",
|
||||||
notificationsModal: "Notifications",
|
notificationsModal: "Notifications",
|
||||||
noNotifications: "No Notifications",
|
noNotifications: "No Notifications",
|
||||||
|
markAllRead: "Mark all as read",
|
||||||
|
lookCopiedBy: "copied your look",
|
||||||
userSavedLook: "saved your look",
|
userSavedLook: "saved your look",
|
||||||
inTheirCloset: "in their closet!",
|
inTheirCloset: "in their closet!",
|
||||||
sharedLookTitle: "Shared Look",
|
sharedLookTitle: "Shared Look",
|
||||||
@@ -493,6 +497,8 @@ export const translations = {
|
|||||||
sendMessage: "Enviar Mensaje",
|
sendMessage: "Enviar Mensaje",
|
||||||
notificationsModal: "Notificaciones",
|
notificationsModal: "Notificaciones",
|
||||||
noNotifications: "Sin Notificaciones",
|
noNotifications: "Sin Notificaciones",
|
||||||
|
markAllRead: "Marcar todas como leídas",
|
||||||
|
lookCopiedBy: "copió tu look",
|
||||||
userSavedLook: "guardó tu look",
|
userSavedLook: "guardó tu look",
|
||||||
inTheirCloset: "en su armario!",
|
inTheirCloset: "en su armario!",
|
||||||
sharedLookTitle: "Look Compartido",
|
sharedLookTitle: "Look Compartido",
|
||||||
@@ -665,6 +671,8 @@ export const translations = {
|
|||||||
sendMessage: "Envoyer le Message",
|
sendMessage: "Envoyer le Message",
|
||||||
notificationsModal: "Notifications",
|
notificationsModal: "Notifications",
|
||||||
noNotifications: "Aucune Notification",
|
noNotifications: "Aucune Notification",
|
||||||
|
markAllRead: "Tout marquer comme lu",
|
||||||
|
lookCopiedBy: "a copié votre look",
|
||||||
userSavedLook: "a sauvegardé votre look",
|
userSavedLook: "a sauvegardé votre look",
|
||||||
inTheirCloset: "dans son placard !",
|
inTheirCloset: "dans son placard !",
|
||||||
sharedLookTitle: "Look Partagé",
|
sharedLookTitle: "Look Partagé",
|
||||||
@@ -837,6 +845,8 @@ export const translations = {
|
|||||||
sendMessage: "Nachricht Senden",
|
sendMessage: "Nachricht Senden",
|
||||||
notificationsModal: "Benachrichtigungen",
|
notificationsModal: "Benachrichtigungen",
|
||||||
noNotifications: "Keine Benachrichtigungen",
|
noNotifications: "Keine Benachrichtigungen",
|
||||||
|
markAllRead: "Alle als gelesen markieren",
|
||||||
|
lookCopiedBy: "hat deinen Look kopiert",
|
||||||
userSavedLook: "hat deinen Look gespeichert",
|
userSavedLook: "hat deinen Look gespeichert",
|
||||||
inTheirCloset: "in seinem Schrank!",
|
inTheirCloset: "in seinem Schrank!",
|
||||||
sharedLookTitle: "Geteilter Look",
|
sharedLookTitle: "Geteilter Look",
|
||||||
|
|||||||
Reference in New Issue
Block a user