Refactor: Split InstanceCard into smaller components

Refactors the InstanceCard component into smaller, more manageable components to improve code organization and readability. This includes extracting parts of the UI and logic into separate components.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-20 17:03:04 +00:00
parent d9a804c36a
commit 4da7b28d1a
6 changed files with 256 additions and 130 deletions

View File

@ -0,0 +1,55 @@
import React from "react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
interface ConfirmationDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
title: string;
description: string;
actionLabel: string;
onAction: () => Promise<void>;
loading: boolean;
}
const ConfirmationDialog = ({
open,
onOpenChange,
title,
description,
actionLabel,
onAction,
loading,
}: ConfirmationDialogProps) => {
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
<AlertDialogDescription>{description}</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading}>Cancelar</AlertDialogCancel>
<AlertDialogAction
onClick={onAction}
disabled={loading}
className={loading ? "opacity-50 cursor-not-allowed" : ""}
>
{loading ? "Processando..." : actionLabel}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};
export default ConfirmationDialog;

View File

@ -0,0 +1,104 @@
import React from "react";
import { Button } from "@/components/ui/button";
import {
QrCode,
RefreshCw,
PowerOff,
CircleDot,
CircleOff,
X
} from "lucide-react";
import { WhatsAppInstance } from "@/types/whatsAppTypes";
interface InstanceActionsProps {
instance: WhatsAppInstance;
loading: string | null;
onViewQrCode: (instance: WhatsAppInstance) => void;
onRestart: () => void;
onLogout: () => void;
onDelete: () => void;
onSetOnline: () => void;
onSetOffline: () => void;
}
const InstanceActions = ({
instance,
loading,
onViewQrCode,
onRestart,
onLogout,
onDelete,
onSetOnline,
onSetOffline,
}: InstanceActionsProps) => {
return (
<>
<div className="flex justify-between w-full">
<Button
variant="outline"
size="sm"
onClick={() => onViewQrCode(instance)}
className="flex items-center"
>
<QrCode className="h-4 w-4 mr-2" />
Ver QR Code
</Button>
<Button
variant="ghost"
size="sm"
className="text-red-500 hover:text-red-700 hover:bg-red-50"
onClick={onDelete}
disabled={loading !== null}
>
<X className="h-4 w-4 mr-2" />
Excluir
</Button>
</div>
<div className="grid grid-cols-2 gap-2 w-full">
<Button
variant="outline"
size="sm"
onClick={onRestart}
disabled={loading !== null}
>
<RefreshCw className="h-4 w-4 mr-1" />
Reiniciar
</Button>
<Button
variant="outline"
size="sm"
onClick={onLogout}
disabled={loading !== null}
>
<PowerOff className="h-4 w-4 mr-1" />
Desconectar
</Button>
</div>
<div className="grid grid-cols-2 gap-2 w-full">
<Button
variant="outline"
size="sm"
onClick={onSetOnline}
disabled={loading !== null}
className="bg-green-50 hover:bg-green-100 text-green-700"
>
<CircleDot className="h-4 w-4 mr-1" />
Definir Online
</Button>
<Button
variant="outline"
size="sm"
onClick={onSetOffline}
disabled={loading !== null}
className="bg-red-50 hover:bg-red-100 text-red-700"
>
<CircleOff className="h-4 w-4 mr-1" />
Definir Offline
</Button>
</div>
</>
);
};
export default InstanceActions;

View File

@ -1,28 +1,11 @@
import { useState } from 'react';
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import {
QrCode,
Smartphone,
RefreshCw,
X,
PowerOff,
CircleDot,
CircleOff
} from 'lucide-react';
import { Smartphone } from 'lucide-react';
import { WhatsAppInstance } from '@/types/whatsAppTypes';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import StatusBadge from './StatusBadge';
import InstanceActions from './InstanceActions';
import ConfirmationDialog from './ConfirmationDialog';
interface InstanceCardProps {
instance: WhatsAppInstance;
@ -175,112 +158,35 @@ const InstanceCard = ({
<span>{instance.phoneNumber}</span>
</div>
<div className="flex items-center text-sm">
{connectionStatus === 'open' ? (
<Badge variant="outline" className="flex items-center gap-1 text-green-500 border-green-300">
<span className="inline-block w-2 h-2 rounded-full bg-green-500"></span>
Status: Conectado
</Badge>
) : connectionStatus === 'connecting' ? (
<Badge variant="outline" className="flex items-center gap-1 text-orange-500 border-orange-300">
<span className="inline-block w-2 h-2 rounded-full bg-orange-500"></span>
Status: Conectando
</Badge>
) : (
<Badge variant="outline" className="flex items-center gap-1 text-red-500 border-red-300">
<span className="inline-block w-2 h-2 rounded-full bg-red-500"></span>
Status: Desconectado
</Badge>
)}
<StatusBadge connectionStatus={connectionStatus} />
</div>
</div>
</CardContent>
<CardFooter className="flex flex-col gap-2">
<div className="flex justify-between w-full">
<Button
variant="outline"
size="sm"
onClick={() => onViewQrCode(instance)}
className="flex items-center"
>
<QrCode className="h-4 w-4 mr-2" />
Ver QR Code
</Button>
<Button
variant="ghost"
size="sm"
className="text-red-500 hover:text-red-700 hover:bg-red-50"
onClick={handleDelete}
disabled={loading !== null}
>
<X className="h-4 w-4 mr-2" />
Excluir
</Button>
</div>
<div className="grid grid-cols-2 gap-2 w-full">
<Button
variant="outline"
size="sm"
onClick={handleRestart}
disabled={loading !== null}
>
<RefreshCw className="h-4 w-4 mr-1" />
Reiniciar
</Button>
<Button
variant="outline"
size="sm"
onClick={handleLogout}
disabled={loading !== null}
>
<PowerOff className="h-4 w-4 mr-1" />
Desconectar
</Button>
</div>
<div className="grid grid-cols-2 gap-2 w-full">
<Button
variant="outline"
size="sm"
onClick={handleSetOnline}
disabled={loading !== null}
className="bg-green-50 hover:bg-green-100 text-green-700"
>
<CircleDot className="h-4 w-4 mr-1" />
Definir Online
</Button>
<Button
variant="outline"
size="sm"
onClick={handleSetOffline}
disabled={loading !== null}
className="bg-red-50 hover:bg-red-100 text-red-700"
>
<CircleOff className="h-4 w-4 mr-1" />
Definir Offline
</Button>
</div>
<InstanceActions
instance={instance}
loading={loading}
onViewQrCode={onViewQrCode}
onRestart={handleRestart}
onLogout={handleLogout}
onDelete={handleDelete}
onSetOnline={handleSetOnline}
onSetOffline={handleSetOffline}
/>
</CardFooter>
</Card>
<AlertDialog open={confirmAction?.open} onOpenChange={(open) => !open && setConfirmAction(null)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{confirmAction?.title}</AlertDialogTitle>
<AlertDialogDescription>
{confirmAction?.description}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading !== null}>Cancelar</AlertDialogCancel>
<AlertDialogAction
onClick={executeAction}
disabled={loading !== null}
className={loading === confirmAction?.title ? "opacity-50 cursor-not-allowed" : ""}
>
{loading === confirmAction?.title ? "Processando..." : confirmAction?.actionLabel}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{confirmAction && (
<ConfirmationDialog
open={confirmAction.open}
onOpenChange={(open) => !open && setConfirmAction(null)}
title={confirmAction.title}
description={confirmAction.description}
actionLabel={confirmAction.actionLabel}
onAction={executeAction}
loading={loading !== null}
/>
)}
</>
);
};

View File

@ -3,13 +3,31 @@ import React from 'react';
interface LoadingStateProps {
message?: string;
size?: 'small' | 'medium' | 'large';
className?: string;
}
const LoadingState = ({ message = "Buscando instância..." }: LoadingStateProps) => {
const LoadingState = ({
message = "Buscando instância...",
size = 'medium',
className = ''
}: LoadingStateProps) => {
const spinnerSizes = {
small: 'h-6 w-6',
medium: 'h-12 w-12',
large: 'h-16 w-16'
};
const textSizes = {
small: 'text-sm',
medium: 'text-base',
large: 'text-lg'
};
return (
<div className="flex justify-center items-center p-10">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
<p className="ml-3">{message}</p>
<div className={`flex justify-center items-center p-4 ${className}`}>
<div className={`animate-spin rounded-full ${spinnerSizes[size]} border-b-2 border-primary`}></div>
{message && <p className={`ml-3 ${textSizes[size]}`}>{message}</p>}
</div>
);
};

View File

@ -0,0 +1,34 @@
import React from "react";
import { Badge } from "@/components/ui/badge";
interface StatusBadgeProps {
connectionStatus: string;
}
const StatusBadge = ({ connectionStatus }: StatusBadgeProps) => {
if (connectionStatus === 'open') {
return (
<Badge variant="outline" className="flex items-center gap-1 text-green-500 border-green-300">
<span className="inline-block w-2 h-2 rounded-full bg-green-500"></span>
Status: Conectado
</Badge>
);
} else if (connectionStatus === 'connecting') {
return (
<Badge variant="outline" className="flex items-center gap-1 text-orange-500 border-orange-300">
<span className="inline-block w-2 h-2 rounded-full bg-orange-500"></span>
Status: Conectando
</Badge>
);
} else {
return (
<Badge variant="outline" className="flex items-center gap-1 text-red-500 border-red-300">
<span className="inline-block w-2 h-2 rounded-full bg-red-500"></span>
Status: Desconectado
</Badge>
);
}
};
export default StatusBadge;

View File

@ -1,10 +1,14 @@
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
export const usePeriodicStatusCheck = (
instancesCount: number,
checkAllInstancesStatus: () => Promise<void>
checkAllInstancesStatus: () => Promise<void>,
initialDelay: number = 1000,
checkInterval: number = 30000
) => {
const isFirstRun = useRef(true);
// Set up periodic status checks
useEffect(() => {
console.log("Setting up periodic status checks, current instances:", instancesCount);
@ -15,16 +19,17 @@ export const usePeriodicStatusCheck = (
initialCheck = setTimeout(() => {
console.log("Running initial status check");
checkAllInstancesStatus();
}, 1000);
isFirstRun.current = false;
}, initialDelay);
}
// Set up interval for periodic checks (every 30 seconds)
// Set up interval for periodic checks
const interval = setInterval(() => {
if (instancesCount > 0) {
console.log("Running periodic status check");
checkAllInstancesStatus();
}
}, 30000); // 30 seconds
}, checkInterval);
// Clean up interval and timeout when component unmounts
return () => {
@ -33,5 +38,9 @@ export const usePeriodicStatusCheck = (
clearTimeout(initialCheck);
}
};
}, [instancesCount, checkAllInstancesStatus]);
}, [instancesCount, checkAllInstancesStatus, initialDelay, checkInterval]);
return {
isFirstRun: isFirstRun.current
};
};