arcadiasuite/client/src/pages/XosCentral.tsx

698 lines
31 KiB
TypeScript

import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Link } from "wouter";
import { BrowserFrame } from "@/components/Browser/BrowserFrame";
import {
Users, Building2, TrendingUp, MessageSquare, Ticket, Zap, LayoutGrid,
ChevronRight, PlusCircle, Filter, Search, Bell, Calendar, Target,
BarChart3, DollarSign, Clock, AlertCircle, Phone, Mail, ArrowUpRight,
Activity, Hash, Shield
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
interface XosStats {
total_contacts: number;
total_leads: number;
total_customers: number;
total_companies: number;
open_deals: number;
won_deals: number;
pipeline_value: number;
won_value: number;
open_conversations: number;
open_tickets: number;
overdue_activities: number;
}
interface Contact {
id: number;
name: string;
email: string;
phone: string;
company: string;
position: string;
type: string;
lead_status: string;
source: string;
created_at: string;
}
interface Deal {
id: number;
title: string;
value: number;
status: string;
stage_name: string;
stage_color: string;
contact_name: string;
company_name: string;
created_at: string;
}
interface Activity {
id: number;
type: string;
title: string;
status: string;
priority: string;
due_at: string;
contact_name: string;
deal_title: string;
}
export default function XosCentral() {
const [activeTab, setActiveTab] = useState("dashboard");
const [isNewContactOpen, setIsNewContactOpen] = useState(false);
const [isNewActivityOpen, setIsNewActivityOpen] = useState(false);
const [newContact, setNewContact] = useState({ name: "", email: "", phone: "", company: "", position: "" });
const [newActivity, setNewActivity] = useState({ type: "task", title: "", due_at: "", priority: "medium" });
const queryClient = useQueryClient();
const createContactMutation = useMutation({
mutationFn: async (data: typeof newContact) => {
const res = await fetch("/api/xos/contacts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error("Erro ao criar contato");
return res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["/api/xos/contacts"] });
queryClient.invalidateQueries({ queryKey: ["/api/xos/stats"] });
setIsNewContactOpen(false);
setNewContact({ name: "", email: "", phone: "", company: "", position: "" });
},
});
const createActivityMutation = useMutation({
mutationFn: async (data: typeof newActivity) => {
const res = await fetch("/api/xos/activities", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error("Erro ao criar atividade");
return res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["/api/xos/activities"] });
queryClient.invalidateQueries({ queryKey: ["/api/xos/stats"] });
setIsNewActivityOpen(false);
setNewActivity({ type: "task", title: "", due_at: "", priority: "medium" });
},
});
const { data: stats } = useQuery<XosStats>({
queryKey: ["/api/xos/stats"],
});
const { data: contacts = [] } = useQuery<Contact[]>({
queryKey: ["/api/xos/contacts"],
});
const { data: deals = [] } = useQuery<Deal[]>({
queryKey: ["/api/xos/deals"],
});
const { data: activities = [] } = useQuery<Activity[]>({
queryKey: ["/api/xos/activities"],
});
const formatCurrency = (value: number) => {
return new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(value || 0);
};
const modules = [
{ id: "crm", name: "CRM", icon: Users, href: "/xos/crm", color: "bg-blue-100 text-blue-600", description: "Gestão de leads e clientes" },
{ id: "inbox", name: "Inbox", icon: MessageSquare, href: "/xos/inbox", color: "bg-green-100 text-green-600", description: "Central de atendimento" },
{ id: "tickets", name: "Tickets", icon: Ticket, href: "/xos/tickets", color: "bg-orange-100 text-orange-600", description: "Suporte e chamados" },
{ id: "automations", name: "Automações", icon: Zap, href: "/xos/automations", color: "bg-violet-100 text-violet-600", description: "Workflows automáticos" },
{ id: "campaigns", name: "Campanhas", icon: Target, href: "/xos/campaigns", color: "bg-pink-100 text-pink-600", description: "Marketing automation" },
{ id: "sites", name: "Sites", icon: LayoutGrid, href: "/xos/sites", color: "bg-cyan-100 text-cyan-600", description: "Site builder" },
{ id: "supervisor", name: "Supervisor", icon: Activity, href: "/xos/supervisor", color: "bg-indigo-100 text-indigo-600", description: "Monitor em tempo real" },
{ id: "reports", name: "Relatórios", icon: BarChart3, href: "/xos/reports", color: "bg-emerald-100 text-emerald-600", description: "CSAT, SLA e KPIs" },
{ id: "protocols", name: "Protocolos", icon: Hash, href: "/xos/protocols", color: "bg-teal-100 text-teal-600", description: "Rastreamento de atendimentos" },
];
const getStatusColor = (status: string) => {
const colors: Record<string, string> = {
new: "bg-blue-100 text-blue-800",
contacted: "bg-indigo-100 text-indigo-800",
qualified: "bg-violet-100 text-violet-800",
proposal: "bg-purple-100 text-purple-800",
won: "bg-green-100 text-green-800",
lost: "bg-red-100 text-red-800",
pending: "bg-yellow-100 text-yellow-800",
completed: "bg-green-100 text-green-800",
};
return colors[status] || "bg-gray-100 text-gray-800";
};
const getStageColor = (color: string | null) => {
const colorMap: Record<string, string> = {
blue: "bg-blue-500",
green: "bg-green-500",
yellow: "bg-yellow-500",
red: "bg-red-500",
purple: "bg-purple-500",
indigo: "bg-indigo-500",
pink: "bg-pink-500",
orange: "bg-orange-500",
cyan: "bg-cyan-500",
emerald: "bg-emerald-500",
violet: "bg-violet-500",
slate: "bg-slate-500",
};
return colorMap[color || 'slate'] || "bg-slate-500";
};
const getActivityIcon = (type: string) => {
const icons: Record<string, any> = {
call: Phone,
email: Mail,
meeting: Calendar,
task: Target,
};
return icons[type] || Calendar;
};
return (
<BrowserFrame>
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50">
{/* Header */}
<header className="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b shadow-sm">
<div className="max-w-7xl mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="bg-gradient-to-br from-blue-600 to-indigo-700 p-2 rounded-xl shadow-lg">
<LayoutGrid className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-indigo-600 bg-clip-text text-transparent">
XOS
</h1>
<p className="text-xs text-slate-500">Experience Operating System</p>
</div>
</div>
<div className="flex items-center gap-3">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" />
<Input
placeholder="Buscar contatos, deals, tickets..."
className="w-80 pl-10 bg-white/80"
data-testid="input-search"
/>
</div>
<Button variant="ghost" size="icon" className="relative" data-testid="button-notifications">
<Bell className="h-5 w-5" />
{stats && stats.overdue_activities > 0 && (
<span className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center">
{stats.overdue_activities}
</span>
)}
</Button>
<Button data-testid="button-new-contact" onClick={() => setIsNewContactOpen(true)}>
<PlusCircle className="h-4 w-4 mr-2" />
Novo Contato
</Button>
</div>
</div>
</div>
</header>
<main className="max-w-7xl mx-auto px-4 py-6">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="mb-6">
<TabsTrigger value="dashboard" data-testid="tab-dashboard">Dashboard</TabsTrigger>
<TabsTrigger value="contacts" data-testid="tab-contacts">Contatos</TabsTrigger>
<TabsTrigger value="deals" data-testid="tab-deals">Negócios</TabsTrigger>
<TabsTrigger value="activities" data-testid="tab-activities">Atividades</TabsTrigger>
</TabsList>
<TabsContent value="dashboard" className="space-y-6">
{/* Stats Cards */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<Card className="bg-gradient-to-br from-blue-500 to-blue-600 text-white" data-testid="card-contacts">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-blue-100 text-sm">Contatos</p>
<p className="text-3xl font-bold">{stats?.total_contacts || 0}</p>
</div>
<Users className="h-10 w-10 text-blue-200" />
</div>
<div className="mt-2 flex items-center gap-2 text-sm">
<span className="text-blue-100">{stats?.total_leads || 0} leads</span>
<span></span>
<span className="text-blue-100">{stats?.total_customers || 0} clientes</span>
</div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-emerald-500 to-emerald-600 text-white" data-testid="card-pipeline">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-emerald-100 text-sm">Pipeline</p>
<p className="text-3xl font-bold">{formatCurrency(Number(stats?.pipeline_value) || 0)}</p>
</div>
<TrendingUp className="h-10 w-10 text-emerald-200" />
</div>
<div className="mt-2 text-sm text-emerald-100">
{stats?.open_deals || 0} negócios abertos
</div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-violet-500 to-violet-600 text-white" data-testid="card-conversations">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-violet-100 text-sm">Conversas</p>
<p className="text-3xl font-bold">{stats?.open_conversations || 0}</p>
</div>
<MessageSquare className="h-10 w-10 text-violet-200" />
</div>
<div className="mt-2 text-sm text-violet-100">
atendimentos em aberto
</div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-orange-500 to-orange-600 text-white" data-testid="card-tickets">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-orange-100 text-sm">Tickets</p>
<p className="text-3xl font-bold">{stats?.open_tickets || 0}</p>
</div>
<Ticket className="h-10 w-10 text-orange-200" />
</div>
<div className="mt-2 text-sm text-orange-100">
chamados pendentes
</div>
</CardContent>
</Card>
</div>
{/* Modules Grid */}
<div>
<h2 className="text-lg font-semibold text-slate-800 mb-4">Módulos XOS</h2>
<div className="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-9 gap-4">
{modules.map((mod) => (
<Link key={mod.id} href={mod.href}>
<Card className="hover:shadow-lg transition-all cursor-pointer group" data-testid={`card-module-${mod.id}`}>
<CardContent className="p-4 text-center">
<div className={`mx-auto w-12 h-12 rounded-xl ${mod.color.split(' ')[0]} flex items-center justify-center mb-3 group-hover:scale-110 transition-transform`}>
<mod.icon className={`h-6 w-6 ${mod.color.split(' ')[1]}`} />
</div>
<p className="font-medium text-slate-800">{mod.name}</p>
<p className="text-xs text-slate-500 mt-1">{mod.description}</p>
</CardContent>
</Card>
</Link>
))}
</div>
</div>
{/* Recent Items */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Recent Contacts */}
<Card data-testid="card-recent-contacts">
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">Contatos Recentes</CardTitle>
<Link href="/xos/crm">
<Button variant="ghost" size="sm">
Ver todos <ChevronRight className="h-4 w-4 ml-1" />
</Button>
</Link>
</div>
</CardHeader>
<CardContent>
<div className="space-y-3">
{contacts.slice(0, 5).map((contact) => (
<div key={contact.id} className="flex items-center gap-3 p-2 hover:bg-slate-50 rounded-lg transition-colors" data-testid={`row-contact-${contact.id}`}>
<Avatar className="h-10 w-10">
<AvatarFallback className="bg-gradient-to-br from-blue-500 to-indigo-500 text-white">
{contact.name.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<p className="font-medium text-slate-800 truncate">{contact.name}</p>
<p className="text-sm text-slate-500 truncate">{contact.company} {contact.position}</p>
</div>
<Badge className={getStatusColor(contact.lead_status)}>{contact.lead_status}</Badge>
</div>
))}
{contacts.length === 0 && (
<p className="text-center text-slate-500 py-4">Nenhum contato ainda</p>
)}
</div>
</CardContent>
</Card>
{/* Recent Deals */}
<Card data-testid="card-recent-deals">
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">Negócios Recentes</CardTitle>
<Link href="/xos/crm">
<Button variant="ghost" size="sm">
Ver todos <ChevronRight className="h-4 w-4 ml-1" />
</Button>
</Link>
</div>
</CardHeader>
<CardContent>
<div className="space-y-3">
{deals.slice(0, 5).map((deal) => (
<div key={deal.id} className="flex items-center gap-3 p-2 hover:bg-slate-50 rounded-lg transition-colors" data-testid={`row-deal-${deal.id}`}>
<div className={`w-2 h-10 rounded-full ${getStageColor(deal.stage_color)}`} />
<div className="flex-1 min-w-0">
<p className="font-medium text-slate-800 truncate">{deal.title}</p>
<p className="text-sm text-slate-500 truncate">{deal.contact_name || deal.company_name}</p>
</div>
<div className="text-right">
<p className="font-semibold text-emerald-600">{formatCurrency(deal.value)}</p>
<p className="text-xs text-slate-500">{deal.stage_name}</p>
</div>
</div>
))}
{deals.length === 0 && (
<p className="text-center text-slate-500 py-4">Nenhum negócio ainda</p>
)}
</div>
</CardContent>
</Card>
</div>
{/* Activities */}
<Card data-testid="card-activities">
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">Atividades Pendentes</CardTitle>
<Button variant="ghost" size="sm">
Ver todas <ChevronRight className="h-4 w-4 ml-1" />
</Button>
</div>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
{activities.slice(0, 6).map((activity) => {
const Icon = getActivityIcon(activity.type);
return (
<div key={activity.id} className="flex items-start gap-3 p-3 bg-slate-50 rounded-lg" data-testid={`row-activity-${activity.id}`}>
<div className="p-2 bg-white rounded-lg shadow-sm">
<Icon className="h-4 w-4 text-slate-600" />
</div>
<div className="flex-1 min-w-0">
<p className="font-medium text-slate-800 text-sm truncate">{activity.title}</p>
<p className="text-xs text-slate-500">{activity.contact_name}</p>
{activity.due_at && (
<div className="flex items-center gap-1 mt-1 text-xs text-slate-500">
<Clock className="h-3 w-3" />
{new Date(activity.due_at).toLocaleDateString("pt-BR")}
</div>
)}
</div>
<Badge variant={activity.priority === "high" ? "destructive" : "secondary"} className="text-xs">
{activity.priority}
</Badge>
</div>
);
})}
</div>
{activities.length === 0 && (
<p className="text-center text-slate-500 py-4">Nenhuma atividade pendente</p>
)}
</CardContent>
</Card>
</TabsContent>
<TabsContent value="contacts">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Todos os Contatos</CardTitle>
<div className="flex gap-2">
<Button variant="outline" size="sm">
<Filter className="h-4 w-4 mr-2" />
Filtros
</Button>
<Button size="sm" onClick={() => setIsNewContactOpen(true)}>
<PlusCircle className="h-4 w-4 mr-2" />
Novo Contato
</Button>
</div>
</div>
</CardHeader>
<CardContent>
<div className="space-y-2">
{contacts.map((contact) => (
<div key={contact.id} className="flex items-center gap-4 p-3 hover:bg-slate-50 rounded-lg border transition-colors" data-testid={`row-contact-full-${contact.id}`}>
<Avatar className="h-12 w-12">
<AvatarFallback className="bg-gradient-to-br from-blue-500 to-indigo-500 text-white text-lg">
{contact.name.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
<div className="flex-1">
<p className="font-semibold text-slate-800">{contact.name}</p>
<p className="text-sm text-slate-500">{contact.company} {contact.position}</p>
</div>
<div className="text-right">
<p className="text-sm text-slate-600">{contact.email}</p>
<p className="text-sm text-slate-500">{contact.phone}</p>
</div>
<Badge className={getStatusColor(contact.lead_status)}>{contact.lead_status}</Badge>
<Badge variant="outline">{contact.source}</Badge>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="deals">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Pipeline de Vendas</CardTitle>
<Link href="/xos/crm">
<Button>
Abrir Pipeline <ArrowUpRight className="h-4 w-4 ml-2" />
</Button>
</Link>
</div>
<CardDescription>Visualize e gerencie todos os seus negócios no modo Kanban</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{deals.map((deal) => (
<div key={deal.id} className="flex items-center gap-4 p-3 hover:bg-slate-50 rounded-lg border transition-colors" data-testid={`row-deal-full-${deal.id}`}>
<div className={`w-2 h-12 rounded-full ${getStageColor(deal.stage_color)}`} />
<div className="flex-1">
<p className="font-semibold text-slate-800">{deal.title}</p>
<p className="text-sm text-slate-500">{deal.contact_name} {deal.company_name}</p>
</div>
<div className="text-right">
<p className="font-bold text-lg text-emerald-600">{formatCurrency(deal.value)}</p>
<p className="text-sm text-slate-500">{deal.stage_name}</p>
</div>
<Badge className={getStatusColor(deal.status)}>{deal.status}</Badge>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="activities">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Atividades</CardTitle>
<Button size="sm" onClick={() => setIsNewActivityOpen(true)}>
<PlusCircle className="h-4 w-4 mr-2" />
Nova Atividade
</Button>
</div>
</CardHeader>
<CardContent>
<div className="space-y-2">
{activities.map((activity) => {
const Icon = getActivityIcon(activity.type);
return (
<div key={activity.id} className="flex items-center gap-4 p-3 hover:bg-slate-50 rounded-lg border transition-colors" data-testid={`row-activity-full-${activity.id}`}>
<div className="p-3 bg-slate-100 rounded-lg">
<Icon className="h-5 w-5 text-slate-600" />
</div>
<div className="flex-1">
<p className="font-semibold text-slate-800">{activity.title}</p>
<p className="text-sm text-slate-500">{activity.contact_name} {activity.deal_title && `${activity.deal_title}`}</p>
</div>
{activity.due_at && (
<div className="flex items-center gap-2 text-slate-500">
<Calendar className="h-4 w-4" />
{new Date(activity.due_at).toLocaleDateString("pt-BR")}
</div>
)}
<Badge className={getStatusColor(activity.status)}>{activity.status}</Badge>
<Badge variant={activity.priority === "high" ? "destructive" : "secondary"}>
{activity.priority}
</Badge>
</div>
);
})}
{activities.length === 0 && (
<p className="text-center text-slate-500 py-8">Nenhuma atividade cadastrada</p>
)}
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</main>
</div>
{/* Modal: Novo Contato */}
<Dialog open={isNewContactOpen} onOpenChange={setIsNewContactOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Novo Contato</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label>Nome *</Label>
<Input
value={newContact.name}
onChange={(e) => setNewContact({ ...newContact, name: e.target.value })}
placeholder="Nome do contato"
/>
</div>
<div className="space-y-2">
<Label>Email</Label>
<Input
type="email"
value={newContact.email}
onChange={(e) => setNewContact({ ...newContact, email: e.target.value })}
placeholder="email@exemplo.com"
/>
</div>
<div className="space-y-2">
<Label>Telefone</Label>
<Input
value={newContact.phone}
onChange={(e) => setNewContact({ ...newContact, phone: e.target.value })}
placeholder="(00) 00000-0000"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Empresa</Label>
<Input
value={newContact.company}
onChange={(e) => setNewContact({ ...newContact, company: e.target.value })}
placeholder="Empresa"
/>
</div>
<div className="space-y-2">
<Label>Cargo</Label>
<Input
value={newContact.position}
onChange={(e) => setNewContact({ ...newContact, position: e.target.value })}
placeholder="Cargo"
/>
</div>
</div>
<Button
className="w-full"
onClick={() => createContactMutation.mutate(newContact)}
disabled={!newContact.name || createContactMutation.isPending}
>
{createContactMutation.isPending ? "Salvando..." : "Criar Contato"}
</Button>
</div>
</DialogContent>
</Dialog>
{/* Modal: Nova Atividade */}
<Dialog open={isNewActivityOpen} onOpenChange={setIsNewActivityOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Nova Atividade</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label>Título *</Label>
<Input
value={newActivity.title}
onChange={(e) => setNewActivity({ ...newActivity, title: e.target.value })}
placeholder="Descrição da atividade"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Tipo</Label>
<Select value={newActivity.type} onValueChange={(v) => setNewActivity({ ...newActivity, type: v })}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="task">Tarefa</SelectItem>
<SelectItem value="call">Ligação</SelectItem>
<SelectItem value="email">E-mail</SelectItem>
<SelectItem value="meeting">Reunião</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>Prioridade</Label>
<Select value={newActivity.priority} onValueChange={(v) => setNewActivity({ ...newActivity, priority: v })}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="low">Baixa</SelectItem>
<SelectItem value="medium">Média</SelectItem>
<SelectItem value="high">Alta</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label>Data prevista</Label>
<Input
type="datetime-local"
value={newActivity.due_at}
onChange={(e) => setNewActivity({ ...newActivity, due_at: e.target.value })}
/>
</div>
<Button
className="w-full"
onClick={() => createActivityMutation.mutate(newActivity)}
disabled={!newActivity.title || createActivityMutation.isPending}
>
{createActivityMutation.isPending ? "Salvando..." : "Criar Atividade"}
</Button>
</div>
</DialogContent>
</Dialog>
</BrowserFrame>
);
}