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:
parent
d9a804c36a
commit
4da7b28d1a
55
src/components/whatsapp/ConfirmationDialog.tsx
Normal file
55
src/components/whatsapp/ConfirmationDialog.tsx
Normal 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;
|
||||
104
src/components/whatsapp/InstanceActions.tsx
Normal file
104
src/components/whatsapp/InstanceActions.tsx
Normal 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;
|
||||
@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
34
src/components/whatsapp/StatusBadge.tsx
Normal file
34
src/components/whatsapp/StatusBadge.tsx
Normal 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;
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user