Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
5 changes: 5 additions & 0 deletions docker_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
VERSION=$1

docker build -t atendai/evolution-manager-v2:${VERSION} .
docker push atendai/evolution-manager-v2:${VERSION}
110 changes: 89 additions & 21 deletions src/pages/Dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { ChevronsUpDown, CircleUser, Cog, MessageCircle, RefreshCw } from "lucide-react";
import {
ChevronsUpDown,
CircleUser,
Cog,
MessageCircle,
RefreshCw,
} from "lucide-react";
import { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
Expand All @@ -8,9 +14,25 @@ import { InstanceStatus } from "@/components/instance-status";
import { InstanceToken } from "@/components/instance-token";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@/components/ui/dialog";
import { DropdownMenu, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import {
Card,
CardContent,
CardFooter,
CardHeader,
} from "@/components/ui/card";
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuCheckboxItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";

import { useFetchInstances } from "@/lib/queries/instance/fetchInstances";
Expand All @@ -23,7 +45,9 @@ import { NewInstance } from "./NewInstance";
function Dashboard() {
const { t } = useTranslation();

const [deleteConfirmation, setDeleteConfirmation] = useState<string | null>(null);
const [deleteConfirmation, setDeleteConfirmation] = useState<string | null>(
null,
);
const { deleteInstance, logout } = useManageInstance();
const { data: instances, refetch } = useFetchInstances();
const [deleting, setDeleting] = useState<string[]>([]);
Expand Down Expand Up @@ -58,11 +82,15 @@ function Dashboard() {
const filteredInstances = useMemo(() => {
let instancesList = instances ? [...instances] : [];
if (searchStatus !== "all") {
instancesList = instancesList.filter((instance) => instance.connectionStatus === searchStatus);
instancesList = instancesList.filter(
(instance) => instance.connectionStatus === searchStatus,
);
}

if (nameSearch !== "") {
instancesList = instancesList.filter((instance) => instance.name.toLowerCase().includes(nameSearch.toLowerCase()));
instancesList = instancesList.filter((instance) =>
instance.name.toLowerCase().includes(nameSearch.toLowerCase()),
);
}

return instancesList;
Expand All @@ -88,7 +116,11 @@ function Dashboard() {
</div>
<div className="my-4 flex items-center justify-between gap-3 px-4">
<div className="flex-1">
<Input placeholder={t("dashboard.search")} value={nameSearch} onChange={(e) => setNameSearch(e.target.value)} />
<Input
placeholder={t("dashboard.search")}
value={nameSearch}
onChange={(e) => setNameSearch(e.target.value)}
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
Expand All @@ -105,7 +137,8 @@ function Dashboard() {
if (checked) {
setSearchStatus(status.value);
}
}}>
}}
>
{status.label}
</DropdownMenuCheckboxItem>
))}
Expand All @@ -114,11 +147,14 @@ function Dashboard() {
</div>
<main className="grid gap-6 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{filteredInstances.length > 0 &&
Array.isArray(instances) &&
instances.map((instance: Instance) => (
Array.isArray(filteredInstances) &&
filteredInstances.map((instance: Instance) => (
<Card key={instance.id}>
<CardHeader>
<Link to={`/manager/instance/${instance.id}/dashboard`} className="flex w-full flex-row items-center justify-between gap-4">
<Link
to={`/manager/instance/${instance.id}/dashboard`}
className="flex w-full flex-row items-center justify-between gap-4"
>
<h3 className="text-wrap font-semibold">{instance.name}</h3>
<Button variant="ghost" size="icon">
<Cog className="card-icon" size="20" />
Expand All @@ -136,7 +172,10 @@ function Dashboard() {
</Avatar>
<div className="space-y-1">
<strong>{instance.profileName}</strong>
<p className="text-sm text-muted-foreground">{instance.ownerJid && instance.ownerJid.split("@")[0]}</p>
<p className="text-sm text-muted-foreground">
{instance.ownerJid &&
instance.ownerJid.split("@")[0]}
</p>
</div>
</>
)}
Expand All @@ -145,19 +184,39 @@ function Dashboard() {
<div className="flex items-center justify-end gap-4 text-sm">
<div className="flex flex-col items-center justify-center gap-1">
<CircleUser className="text-muted-foreground" size="20" />
<span>{new Intl.NumberFormat("pt-BR").format(instance?._count?.Contact || 0)}</span>
<span>
{new Intl.NumberFormat("pt-BR").format(
instance?._count?.Contact || 0,
)}
</span>
</div>
<div className="flex flex-col items-center justify-center gap-1">
<MessageCircle className="text-muted-foreground" size="20" />
<span>{new Intl.NumberFormat("pt-BR").format(instance?._count?.Message || 0)}</span>
<MessageCircle
className="text-muted-foreground"
size="20"
/>
<span>
{new Intl.NumberFormat("pt-BR").format(
instance?._count?.Message || 0,
)}
</span>
</div>
</div>
</div>
</CardContent>
<CardFooter className="justify-between">
<InstanceStatus status={instance.connectionStatus} />
<Button variant="destructive" size="sm" onClick={() => setDeleteConfirmation(instance.name)} disabled={deleting.includes(instance.name)}>
{deleting.includes(instance.name) ? <span>{t("button.deleting")}</span> : <span>{t("button.delete")}</span>}
<Button
variant="destructive"
size="sm"
onClick={() => setDeleteConfirmation(instance.name)}
disabled={deleting.includes(instance.name)}
>
{deleting.includes(instance.name) ? (
<span>{t("button.deleting")}</span>
) : (
<span>{t("button.delete")}</span>
)}
</Button>
</CardFooter>
</Card>
Expand All @@ -169,13 +228,22 @@ function Dashboard() {
<DialogContent>
<DialogClose />
<DialogHeader>{t("modal.delete.title")}</DialogHeader>
<p>{t("modal.delete.message", { instanceName: deleteConfirmation })}</p>
<p>
{t("modal.delete.message", { instanceName: deleteConfirmation })}
</p>
<DialogFooter>
<div className="flex items-center gap-4">
<Button onClick={() => setDeleteConfirmation(null)} size="sm" variant="outline">
<Button
onClick={() => setDeleteConfirmation(null)}
size="sm"
variant="outline"
>
{t("button.cancel")}
</Button>
<Button onClick={() => handleDelete(deleteConfirmation)} variant="destructive">
<Button
onClick={() => handleDelete(deleteConfirmation)}
variant="destructive"
>
{t("button.delete")}
</Button>
</div>
Expand Down
Loading
Loading