Initialize fresh project without Clerk

This commit is contained in:
2026-03-14 22:57:39 -03:00
commit 6898297935
401 changed files with 71631 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
import React, { useState, useEffect } from 'react';
import { X, Archive, Trash2, RefreshCcw } from 'lucide-react';
import api from '../services/api';
import type { INotification } from '../types';
interface ArchivedNotificationsModalProps {
isOpen: boolean;
onClose: () => void;
}
export const ArchivedNotificationsModal: React.FC<ArchivedNotificationsModalProps> = ({
isOpen,
onClose,
}) => {
const [notifications, setNotifications] = useState<INotification[]>([]);
const [isLoading, setIsLoading] = useState(false);
const fetchArchived = async () => {
setIsLoading(true);
try {
const response = await api.get<INotification[]>('/notifications?includeArchived=true');
// Filtrar apenas as arquivadas (no frontend por segurança, embora o backend já devesse ajudar)
// Na verdade, passamos includeArchived=true, o backend retornará unread + archived.
// Vamos filtrar para mostrar apenas o "Log" (arquivadas).
setNotifications(response.data.filter(n => n.isArchived || n.archivedBy?.length > 0));
} catch (error) {
console.error('Error fetching archived notifications:', error);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
if (isOpen) {
fetchArchived();
}
}, [isOpen]);
const deleteForever = async (id: string) => {
if (!window.confirm('Excluir permanentemente este registro do log?')) return;
try {
await api.delete(`/notifications/${id}`);
setNotifications(prev => prev.filter(n => n._id !== id));
} catch (error) {
console.error('Error deleting archived notification:', error);
}
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-[60] flex items-center justify-center p-4 animate-in fade-in">
<div className="bg-zinc-900 rounded-2xl shadow-2xl max-w-2xl w-full border border-zinc-800 animate-in slide-in-from-bottom-4 max-h-[80vh] flex flex-col overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-zinc-800 bg-zinc-900">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-zinc-800 flex items-center justify-center">
<Archive className="text-zinc-400" size={20} />
</div>
<div>
<h2 className="text-lg font-bold text-white">Log de Mensagens (Arquivadas)</h2>
<p className="text-sm text-zinc-500">
Histórico de notificações sistema
</p>
</div>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-zinc-800 rounded-lg transition-colors"
>
<X size={20} className="text-zinc-500" />
</button>
</div>
{/* Body */}
<div className="flex-1 overflow-y-auto p-6 space-y-4 bg-zinc-950/50">
{isLoading ? (
<div className="text-center py-8">
<RefreshCcw className="animate-spin text-primary mx-auto mb-2" size={24} />
<p className="text-zinc-500">Carregando histórico...</p>
</div>
) : notifications.length === 0 ? (
<div className="text-center py-12">
<Archive size={48} className="text-zinc-800 mx-auto mb-4" />
<p className="text-zinc-500 font-semibold">Nenhuma mensagem arquivada</p>
<p className="text-zinc-600 text-sm mt-1">
Mensagens arquivadas aparecerão aqui para consulta.
</p>
</div>
) : (
<div className="space-y-3">
{notifications.map((msg) => (
<div
key={msg._id}
className="bg-zinc-900 border border-zinc-800 rounded-xl p-4 hover:border-zinc-700 transition-all group"
>
<div className="flex items-start justify-between mb-2">
<div className="flex flex-col">
<span className="font-bold text-zinc-200 text-sm">{msg.title}</span>
<span className="text-[10px] text-zinc-500">
{new Date(msg.createdAt).toLocaleString('pt-BR')}
</span>
</div>
<button
onClick={() => deleteForever(msg._id)}
className="opacity-0 group-hover:opacity-100 p-1.5 text-zinc-600 hover:text-red-500 transition-all"
title="Excluir permanentemente"
>
<Trash2 size={14} />
</button>
</div>
<p className="text-zinc-400 text-xs leading-relaxed">{msg.message}</p>
</div>
))}
</div>
)}
</div>
{/* Footer */}
<div className="p-4 border-t border-zinc-800 bg-zinc-900 flex justify-end">
<button
onClick={onClose}
className="px-6 py-2 bg-zinc-800 hover:bg-zinc-700 text-white rounded-lg font-bold text-sm transition-colors"
>
Fechar
</button>
</div>
</div>
</div>
);
};