feat: Implement user-specific WhatsApp instances

- Associate WhatsApp instances with logged-in users.
- Store user ID/email with instance data.
- Filter instance cards to show only the user's instances.
- Clear instances on logout/login.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-19 14:41:04 +00:00
parent 44e8569b6c
commit 54310bd204
2 changed files with 118 additions and 25 deletions

View File

@ -41,6 +41,12 @@ const Header = () => {
localStorage.removeItem('userName'); localStorage.removeItem('userName');
localStorage.removeItem('userEmail'); localStorage.removeItem('userEmail');
// Dispatch a storage event to notify other components about logout
window.dispatchEvent(new StorageEvent('storage', {
key: 'autenticado',
newValue: null
}));
toast({ toast({
title: "Logout realizado", title: "Logout realizado",
description: "Você foi desconectado com sucesso" description: "Você foi desconectado com sucesso"

View File

@ -15,6 +15,7 @@ interface WhatsAppInstance {
instanceName: string; instanceName: string;
instanceId: string; instanceId: string;
phoneNumber: string; phoneNumber: string;
userId: string;
status?: string; status?: string;
qrcode?: string; qrcode?: string;
connectionState?: 'open' | 'closed' | 'connecting'; connectionState?: 'open' | 'closed' | 'connecting';
@ -36,25 +37,76 @@ const WhatsApp = () => {
const [qrError, setQrError] = useState<string | null>(null); const [qrError, setQrError] = useState<string | null>(null);
const [qrDialogOpen, setQrDialogOpen] = useState(false); const [qrDialogOpen, setQrDialogOpen] = useState(false);
const [statusCheckInterval, setStatusCheckInterval] = useState<NodeJS.Timeout | null>(null); const [statusCheckInterval, setStatusCheckInterval] = useState<NodeJS.Timeout | null>(null);
const [currentUserId, setCurrentUserId] = useState<string>('');
// Load saved instances from localStorage on component mount // Get current user ID on component mount
useEffect(() => {
const userId = localStorage.getItem('userId') || '';
setCurrentUserId(userId);
}, []);
// Load saved instances from localStorage on component mount and filter by current user
useEffect(() => { useEffect(() => {
const savedInstances = localStorage.getItem('whatsappInstances'); const savedInstances = localStorage.getItem('whatsappInstances');
if (savedInstances) { if (savedInstances && currentUserId) {
try { try {
setInstances(JSON.parse(savedInstances)); const allInstances = JSON.parse(savedInstances);
// Filter instances to only show those belonging to the current user
const userInstances = allInstances.filter(
(instance: WhatsAppInstance) => instance.userId === currentUserId
);
setInstances(userInstances);
} catch (error) { } catch (error) {
console.error("Error parsing saved instances:", error); console.error("Error parsing saved instances:", error);
} }
} }
}, []); }, [currentUserId]); // Re-run when currentUserId changes
// Save instances to localStorage whenever they change // Save instances to localStorage whenever they change
useEffect(() => { useEffect(() => {
if (instances.length > 0) { if (instances.length > 0) {
localStorage.setItem('whatsappInstances', JSON.stringify(instances)); // We need to save ALL instances (not just current user's) to maintain everyone's data
const savedInstances = localStorage.getItem('whatsappInstances');
let allInstances: WhatsAppInstance[] = [];
if (savedInstances) {
try {
const parsedInstances = JSON.parse(savedInstances);
// Filter out current user's instances from saved data (we'll add updated ones)
allInstances = parsedInstances.filter(
(instance: WhatsAppInstance) => instance.userId !== currentUserId
);
} catch (error) {
console.error("Error parsing saved instances:", error);
}
}
// Add current user's instances to the array
const updatedInstances = [...allInstances, ...instances];
localStorage.setItem('whatsappInstances', JSON.stringify(updatedInstances));
} }
}, [instances]); }, [instances, currentUserId]);
// Handle user logout event to clear instances display
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'autenticado' && e.newValue === null) {
// User logged out, clear instances from display
setInstances([]);
setCurrentUserId('');
} else if (e.key === 'userId') {
// User ID changed (new login)
const newUserId = localStorage.getItem('userId') || '';
setCurrentUserId(newUserId);
}
};
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}, []);
// Set up periodic status checks // Set up periodic status checks
useEffect(() => { useEffect(() => {
@ -82,18 +134,22 @@ const WhatsApp = () => {
// Function to check connection status for all instances // Function to check connection status for all instances
const checkAllInstancesStatus = async () => { const checkAllInstancesStatus = async () => {
const updatedInstances = await Promise.all( try {
instances.map(async (instance) => { const updatedInstances = await Promise.all(
try { instances.map(async (instance) => {
const state = await fetchConnectionState(instance.instanceName); try {
return { ...instance, connectionState: state }; const state = await fetchConnectionState(instance.instanceName);
} catch (error) { return { ...instance, connectionState: state };
console.error(`Error checking status for ${instance.instanceName}:`, error); } catch (error) {
return { ...instance, connectionState: 'closed' }; console.error(`Error checking status for ${instance.instanceName}:`, error);
} return { ...instance, connectionState: 'closed' as const };
}) }
); })
setInstances(updatedInstances); );
setInstances(updatedInstances);
} catch (error) {
console.error("Error checking instances status:", error);
}
}; };
// Function to fetch connection state for an instance // Function to fetch connection state for an instance
@ -115,7 +171,7 @@ const WhatsApp = () => {
} }
const data = await response.json(); const data = await response.json();
return data.instance?.state || 'closed'; return data.instance?.state || 'closed' as 'open' | 'closed' | 'connecting';
} catch (error) { } catch (error) {
console.error("Error fetching connection state:", error); console.error("Error fetching connection state:", error);
return 'closed'; return 'closed';
@ -142,6 +198,16 @@ const WhatsApp = () => {
return; return;
} }
// Ensure user is logged in
if (!currentUserId) {
toast({
title: "Erro de autenticação",
description: "Você precisa estar logado para criar uma instância",
variant: "destructive",
});
return;
}
setLoading(true); setLoading(true);
try { try {
@ -169,11 +235,12 @@ const WhatsApp = () => {
throw new Error(data.message || 'Erro ao criar instância'); throw new Error(data.message || 'Erro ao criar instância');
} }
// Create new instance object // Create new instance object with user ID
const newInstance: WhatsAppInstance = { const newInstance: WhatsAppInstance = {
instanceName, instanceName,
instanceId: data.instance.instanceId, instanceId: data.instance.instanceId,
phoneNumber, phoneNumber,
userId: currentUserId, // Associate with current user
status: data.instance.status, status: data.instance.status,
qrcode: data.qrcode, qrcode: data.qrcode,
connectionState: 'connecting' connectionState: 'connecting'
@ -238,7 +305,7 @@ const WhatsApp = () => {
} }
// Using the "code" field from the response as the QR code data // Using the "code" field from the response as the QR code data
if (data && data.code) { if (data && data.base64) {
// Save the base64 image data directly - it already contains the data:image prefix // Save the base64 image data directly - it already contains the data:image prefix
setQrCodeData(data.base64); setQrCodeData(data.base64);
} else { } else {
@ -263,9 +330,20 @@ const WhatsApp = () => {
if (activeInstance?.instanceId === instanceId) { if (activeInstance?.instanceId === instanceId) {
setQrDialogOpen(false); setQrDialogOpen(false);
} }
// Update localStorage
const updatedInstances = instances.filter(instance => instance.instanceId !== instanceId); // Update localStorage - need to preserve other users' instances
localStorage.setItem('whatsappInstances', JSON.stringify(updatedInstances)); const savedInstances = localStorage.getItem('whatsappInstances');
if (savedInstances) {
try {
const allInstances = JSON.parse(savedInstances);
const updatedInstances = allInstances.filter(
(instance: WhatsAppInstance) => !(instance.instanceId === instanceId && instance.userId === currentUserId)
);
localStorage.setItem('whatsappInstances', JSON.stringify(updatedInstances));
} catch (error) {
console.error("Error updating instances in localStorage:", error);
}
}
toast({ toast({
title: "Instância removida", title: "Instância removida",
@ -327,7 +405,7 @@ const WhatsApp = () => {
</Card> </Card>
{/* List of created instances */} {/* List of created instances */}
{instances.length > 0 && ( {instances.length > 0 ? (
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-xl font-semibold">Instâncias Criadas</h2> <h2 className="text-xl font-semibold">Instâncias Criadas</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@ -380,6 +458,15 @@ const WhatsApp = () => {
))} ))}
</div> </div>
</div> </div>
) : (
<Card>
<CardContent className="py-6">
<div className="text-center text-muted-foreground">
<p>Nenhuma instância criada ainda.</p>
<p className="mt-1">Crie uma instância usando o formulário acima.</p>
</div>
</CardContent>
</Card>
)} )}
{/* QR Code Dialog */} {/* QR Code Dialog */}