refactor: improve UI

This commit is contained in:
Mauricio Siu
2024-05-17 23:43:07 -06:00
parent 667067811c
commit 5806068e2e
30 changed files with 431 additions and 370 deletions

View File

@@ -25,8 +25,8 @@ export const DeleteApplication = ({ applicationId }: Props) => {
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground" />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>

View File

@@ -24,8 +24,8 @@ export const DeleteDomain = ({ domainId }: Props) => {
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground " />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>

View File

@@ -40,7 +40,9 @@ export const ShowDomains = ({ applicationId }: Props) => {
</div> </div>
{data && data?.length > 0 && ( {data && data?.length > 0 && (
<AddDomain applicationId={applicationId} /> <AddDomain applicationId={applicationId}>
<GlobeIcon className="size-4" /> Add Domain
</AddDomain>
)} )}
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-row gap-4"> <CardContent className="flex w-full flex-row gap-4">
@@ -51,7 +53,9 @@ export const ShowDomains = ({ applicationId }: Props) => {
To access to the application is required to set at least 1 To access to the application is required to set at least 1
domain domain
</span> </span>
<AddDomain applicationId={applicationId}>Add Domain</AddDomain> <AddDomain applicationId={applicationId}>
<GlobeIcon className="size-4" /> Add Domain
</AddDomain>
</div> </div>
) : ( ) : (
<div className="flex w-full flex-col gap-4"> <div className="flex w-full flex-col gap-4">
@@ -75,8 +79,10 @@ export const ShowDomains = ({ applicationId }: Props) => {
<Button variant="outline" disabled> <Button variant="outline" disabled>
{item.https ? "HTTPS" : "HTTP"} {item.https ? "HTTPS" : "HTTP"}
</Button> </Button>
<UpdateDomain domainId={item.domainId} /> <div className="flex flex-row gap-1">
<DeleteDomain domainId={item.domainId} /> <UpdateDomain domainId={item.domainId} />
<DeleteDomain domainId={item.domainId} />
</div>
</div> </div>
); );
})} })}

View File

@@ -115,8 +115,8 @@ export const UpdateDomain = ({ domainId }: Props) => {
return ( return (
<Dialog> <Dialog>
<DialogTrigger className="" asChild> <DialogTrigger className="" asChild>
<Button> <Button variant="ghost">
<PenBoxIcon className="size-4" /> <PenBoxIcon className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">

View File

@@ -90,7 +90,7 @@ export const UpdateApplication = ({ applicationId }: Props) => {
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost"> <Button variant="ghost">
<SquarePen className="size-4" /> <SquarePen className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
AlertDialogCancel, AlertDialogCancel,
AlertDialogContent, AlertDialogContent,
AlertDialogDescription, AlertDialogDescription,
AlertDialogFooter, AlertDialogFooter,
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
mariadbId: string; mariadbId: string;
} }
export const DeleteMariadb = ({ mariadbId }: Props) => { export const DeleteMariadb = ({ mariadbId }: Props) => {
const { mutateAsync, isLoading } = api.mariadb.remove.useMutation(); const { mutateAsync, isLoading } = api.mariadb.remove.useMutation();
const { push } = useRouter(); const { push } = useRouter();
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground " />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
database database
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={async () => { onClick={async () => {
await mutateAsync({ await mutateAsync({
mariadbId, mariadbId,
}) })
.then((data) => { .then((data) => {
push(`/dashboard/project/${data?.projectId}`); push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully"); toast.success("Database delete succesfully");
}) })
.catch(() => { .catch(() => {
toast.error("Error to delete the database"); toast.error("Error to delete the database");
}); });
}} }}
> >
Confirm Confirm
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
); );
}; };

View File

@@ -90,7 +90,7 @@ export const UpdateMariadb = ({ mariadbId }: Props) => {
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost"> <Button variant="ghost">
<SquarePen className="size-4" /> <SquarePen className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
AlertDialogCancel, AlertDialogCancel,
AlertDialogContent, AlertDialogContent,
AlertDialogDescription, AlertDialogDescription,
AlertDialogFooter, AlertDialogFooter,
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
mongoId: string; mongoId: string;
} }
export const DeleteMongo = ({ mongoId }: Props) => { export const DeleteMongo = ({ mongoId }: Props) => {
const { mutateAsync, isLoading } = api.mongo.remove.useMutation(); const { mutateAsync, isLoading } = api.mongo.remove.useMutation();
const { push } = useRouter(); const { push } = useRouter();
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground " />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
database database
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={async () => { onClick={async () => {
await mutateAsync({ await mutateAsync({
mongoId, mongoId,
}) })
.then((data) => { .then((data) => {
push(`/dashboard/project/${data?.projectId}`); push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully"); toast.success("Database delete succesfully");
}) })
.catch(() => { .catch(() => {
toast.error("Error to delete the database"); toast.error("Error to delete the database");
}); });
}} }}
> >
Confirm Confirm
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
); );
}; };

View File

@@ -90,7 +90,7 @@ export const UpdateMongo = ({ mongoId }: Props) => {
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost"> <Button variant="ghost">
<SquarePen className="size-4" /> <SquarePen className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
AlertDialogCancel, AlertDialogCancel,
AlertDialogContent, AlertDialogContent,
AlertDialogDescription, AlertDialogDescription,
AlertDialogFooter, AlertDialogFooter,
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
mysqlId: string; mysqlId: string;
} }
export const DeleteMysql = ({ mysqlId }: Props) => { export const DeleteMysql = ({ mysqlId }: Props) => {
const { mutateAsync, isLoading } = api.mysql.remove.useMutation(); const { mutateAsync, isLoading } = api.mysql.remove.useMutation();
const { push } = useRouter(); const { push } = useRouter();
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground " />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
database database
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={async () => { onClick={async () => {
await mutateAsync({ await mutateAsync({
mysqlId, mysqlId,
}) })
.then((data) => { .then((data) => {
push(`/dashboard/project/${data?.projectId}`); push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully"); toast.success("Database delete succesfully");
}) })
.catch(() => { .catch(() => {
toast.error("Error to delete the database"); toast.error("Error to delete the database");
}); });
}} }}
> >
Confirm Confirm
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
); );
}; };

View File

@@ -90,7 +90,7 @@ export const UpdateMysql = ({ mysqlId }: Props) => {
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost"> <Button variant="ghost">
<SquarePen className="size-4" /> <SquarePen className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
AlertDialogCancel, AlertDialogCancel,
AlertDialogContent, AlertDialogContent,
AlertDialogDescription, AlertDialogDescription,
AlertDialogFooter, AlertDialogFooter,
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
postgresId: string; postgresId: string;
} }
export const DeletePostgres = ({ postgresId }: Props) => { export const DeletePostgres = ({ postgresId }: Props) => {
const { mutateAsync, isLoading } = api.postgres.remove.useMutation(); const { mutateAsync, isLoading } = api.postgres.remove.useMutation();
const { push } = useRouter(); const { push } = useRouter();
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground " />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
database database
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={async () => { onClick={async () => {
await mutateAsync({ await mutateAsync({
postgresId, postgresId,
}) })
.then((data) => { .then((data) => {
push(`/dashboard/project/${data?.projectId}`); push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully"); toast.success("Database delete succesfully");
}) })
.catch(() => { .catch(() => {
toast.error("Error to delete the database"); toast.error("Error to delete the database");
}); });
}} }}
> >
Confirm Confirm
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
); );
}; };

View File

@@ -90,7 +90,7 @@ export const UpdatePostgres = ({ postgresId }: Props) => {
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost"> <Button variant="ghost">
<SquarePen className="size-4" /> <SquarePen className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
AlertDialogCancel, AlertDialogCancel,
AlertDialogContent, AlertDialogContent,
AlertDialogDescription, AlertDialogDescription,
AlertDialogFooter, AlertDialogFooter,
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
redisId: string; redisId: string;
} }
export const DeleteRedis = ({ redisId }: Props) => { export const DeleteRedis = ({ redisId }: Props) => {
const { mutateAsync, isLoading } = api.redis.remove.useMutation(); const { mutateAsync, isLoading } = api.redis.remove.useMutation();
const { push } = useRouter(); const { push } = useRouter();
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 " /> <TrashIcon className="size-4 text-muted-foreground " />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
database database
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={async () => { onClick={async () => {
await mutateAsync({ await mutateAsync({
redisId, redisId,
}) })
.then((data) => { .then((data) => {
push(`/dashboard/project/${data?.projectId}`); push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully"); toast.success("Database delete succesfully");
}) })
.catch(() => { .catch(() => {
toast.error("Error to delete the database"); toast.error("Error to delete the database");
}); });
}} }}
> >
Confirm Confirm
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
); );
}; };

View File

@@ -90,7 +90,7 @@ export const UpdateRedis = ({ redisId }: Props) => {
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost"> <Button variant="ghost">
<SquarePen className="size-4" /> <SquarePen className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -33,21 +33,23 @@ export const ShowCertificates = () => {
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
{data?.map((destination, index) => ( <div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
<div {data?.map((destination, index) => (
key={destination.certificateId} <div
className="flex items-center justify-between" key={destination.certificateId}
> className="flex items-center justify-between border p-4 rounded-lg"
<span className="text-sm text-muted-foreground"> >
{index + 1}. {destination.name} <span className="text-sm text-muted-foreground">
</span> {index + 1}. {destination.name}
<div className="flex flex-row gap-3"> </span>
<DeleteCertificate <div className="flex flex-row gap-3">
certificateId={destination.certificateId} <DeleteCertificate
/> certificateId={destination.certificateId}
/>
</div>
</div> </div>
</div> ))}
))} </div>
<div> <div>
<AddCertificate /> <AddCertificate />
</div> </div>

View File

@@ -27,8 +27,7 @@ export const AddNode = () => {
<DialogTitle>Add Node</DialogTitle> <DialogTitle>Add Node</DialogTitle>
<DialogDescription className="flex flex-col gap-2"> <DialogDescription className="flex flex-col gap-2">
Follow the steps to add a new node to your cluster, before you start Follow the steps to add a new node to your cluster, before you start
working with this feature, you need to understand how docker swarm using this feature, you need to understand how docker swarm works.{" "}
works.{" "}
<Link <Link
href="https://docs.docker.com/engine/swarm/" href="https://docs.docker.com/engine/swarm/"
target="_blank" target="_blank"

View File

@@ -52,16 +52,16 @@ export const ShowRegistry = () => {
</div> </div>
</div> </div>
) : ( ) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 gap-6">
{data?.map((registry, index) => ( {data?.map((registry, index) => (
<div <div
key={registry.registryId} key={registry.registryId}
className="flex items-center justify-between border p-4 rounded-lg hover:bg-muted cursor-pointer" className="flex items-center justify-between border p-4 rounded-lg"
> >
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
{index + 1}. {registry.registryName} {index + 1}. {registry.registryName}
</span> </span>
<div className="flex flex-row gap-3"> <div className="flex flex-row gap-1">
<UpdateDockerRegistry registryId={registry.registryId} /> <UpdateDockerRegistry registryId={registry.registryId} />
<DeleteRegistry registryId={registry.registryId} /> <DeleteRegistry registryId={registry.registryId} />
</div> </div>

View File

@@ -34,16 +34,16 @@ export const ShowDestinations = () => {
<AddDestination /> <AddDestination />
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-4">
{data?.map((destination, index) => ( {data?.map((destination, index) => (
<div <div
key={destination.destinationId} key={destination.destinationId}
className="flex items-center justify-between" className="flex items-center justify-between border p-3.5 rounded-lg"
> >
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
{index + 1}. {destination.name} {index + 1}. {destination.name}
</span> </span>
<div className="flex flex-row gap-3"> <div className="flex flex-row gap-1">
<UpdateDestination <UpdateDestination
destinationId={destination.destinationId} destinationId={destination.destinationId}
/> />

View File

@@ -87,10 +87,10 @@ export const GithubSetup = () => {
{haveGithubConfigured ? ( {haveGithubConfigured ? (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span className="text-muted-foreground"> <span className="text-muted-foreground text-sm">
Github account configured succesfully. Github account configured succesfully.
</span> </span>
<BadgeCheck className="size-5 text-green-700" /> <BadgeCheck className="size-4 text-green-700" />
</div> </div>
<div className="flex items-end"> <div className="flex items-end">
<RemoveGithubApp /> <RemoveGithubApp />
@@ -101,9 +101,9 @@ export const GithubSetup = () => {
{data?.githubAppName ? ( {data?.githubAppName ? (
<div className="flex w-fit flex-col gap-4"> <div className="flex w-fit flex-col gap-4">
<span className="text-muted-foreground"> <span className="text-muted-foreground">
Youve successfully created a GitHub app named You've successfully created a github app named{" "}
{data.githubAppName}! The next step is to install this app in <strong>{data.githubAppName}</strong>! The next step is to
your GitHub account. install this app in your GitHub account.
</span> </span>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-4">
@@ -121,12 +121,12 @@ export const GithubSetup = () => {
) : ( ) : (
<div> <div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-muted-foreground"> <p className="text-muted-foreground text-sm">
To integrate your GitHub account with our services, youll To integrate your GitHub account with our services, you'll
need to create and install a GitHub app. This process is need to create and install a GitHub app. This process is
straightforward and only takes a few minutes. Click the straightforward and only takes a few minutes. Click the
button below to get started. button below to get started.
</span> </p>
</div> </div>
<div className="mt-4 flex flex-col gap-4"> <div className="mt-4 flex flex-col gap-4">

View File

@@ -23,11 +23,11 @@ import { extractServices } from "@/pages/dashboard/project/[projectId]";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { ListTodo } from "lucide-react";
import { useEffect } from "react"; import { useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
const addPermissions = z.object({ const addPermissions = z.object({
accesedProjects: z.array(z.string()).optional(), accesedProjects: z.array(z.string()).optional(),
@@ -107,9 +107,12 @@ export const AddUserPermissions = ({ userId }: Props) => {
return ( return (
<Dialog> <Dialog>
<DialogTrigger className="" asChild> <DialogTrigger className="" asChild>
<Button variant="ghost"> <DropdownMenuItem
<ListTodo className="size-4 text-muted-foreground " /> className="w-full cursor-pointer"
</Button> onSelect={(e) => e.preventDefault()}
>
Add Permissions
</DropdownMenuItem>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-4xl"> <DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-4xl">
<DialogHeader> <DialogHeader>

View File

@@ -25,6 +25,7 @@ import { useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { PlusIcon } from "lucide-react";
const addUser = z.object({ const addUser = z.object({
email: z email: z
@@ -66,7 +67,9 @@ export const AddUser = () => {
return ( return (
<Dialog> <Dialog>
<DialogTrigger className="" asChild> <DialogTrigger className="" asChild>
<Button>Add User</Button> <Button>
<PlusIcon className="h-4 w-4" /> Add User
</Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogHeader> <DialogHeader>

View File

@@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { TrashIcon } from "lucide-react"; import { TrashIcon } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
interface Props { interface Props {
authId: string; authId: string;
@@ -24,9 +25,12 @@ export const DeleteUser = ({ authId }: Props) => {
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}> <DropdownMenuItem
<TrashIcon className="size-4 text-muted-foreground " /> className="w-full cursor-pointer"
</Button> onSelect={(e) => e.preventDefault()}
>
Delete User
</DropdownMenuItem>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>

View File

@@ -6,7 +6,7 @@ import {
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { CopyIcon, Users } from "lucide-react"; import { MoreHorizontal, Users } from "lucide-react";
import { AddUser } from "./add-user"; import { AddUser } from "./add-user";
import { DeleteUser } from "./delete-user"; import { DeleteUser } from "./delete-user";
import { format } from "date-fns"; import { format } from "date-fns";
@@ -14,7 +14,24 @@ import { useEffect, useState } from "react";
import { AddUserPermissions } from "./add-permissions"; import { AddUserPermissions } from "./add-permissions";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { toast } from "sonner"; import { toast } from "sonner";
import { UpdateUser } from "./update-user"; import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
export const ShowUsers = () => { export const ShowUsers = () => {
const { data } = api.user.all.useQuery(); const { data } = api.user.all.useQuery();
@@ -25,82 +42,109 @@ export const ShowUsers = () => {
return ( return (
<div className="h-full col-span-2"> <div className="h-full col-span-2">
<Card className="bg-transparent h-full border-none"> <Card className="bg-transparent h-full ">
<CardHeader> <CardHeader className="flex flex-row gap-2 justify-between w-full flex-wrap">
<CardTitle className="text-xl">Users</CardTitle> <div className="flex flex-col gap-2">
<CardDescription>Add, manage and delete users.</CardDescription> <CardTitle className="text-xl">Users</CardTitle>
<CardDescription>Add, manage and delete users.</CardDescription>
</div>
{data && data.length > 0 && (
<div className="flex flex-col gap-3 items-end">
<AddUser />
</div>
)}
</CardHeader> </CardHeader>
<CardContent className="space-y-2 h-full"> <CardContent className="space-y-2 h-full">
{data?.length === 0 ? ( {data?.length === 0 ? (
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<Users className="size-8 self-center text-muted-foreground" /> <Users className="size-8 self-center text-muted-foreground" />
<span className="text-base text-muted-foreground"> <span className="text-base text-muted-foreground">
To create a user is required to add To create a user, you need to add:
</span> </span>
<AddUser /> <AddUser />
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
{data?.map((user) => { <Table>
return ( <TableCaption>See all users</TableCaption>
<div <TableHeader>
key={user.userId} <TableRow>
className="flex gap-2 flex-col justify-start border p-4 rounded-lg" <TableHead className="w-[100px]">Email</TableHead>
> <TableHead className="text-center">Status</TableHead>
<span className="text-sm text-foreground"> <TableHead className="text-center">2FA</TableHead>
{user.auth.email} <TableHead className="text-center">Expiration</TableHead>
</span> <TableHead className="text-right">Actions</TableHead>
{!user.isRegistered && ( </TableRow>
<span className="text-sm text-muted-foreground"> </TableHeader>
Expire In{" "} <TableBody>
{format(new Date(user.expirationDate), "PPpp")} {data?.map((user) => {
</span> return (
)} <TableRow key={user.userId}>
<TableCell className="w-[100px]">
<span className="text-sm text-muted-foreground"> {user.auth.email}
{user.isRegistered ? "Registered" : "Not Registered"} </TableCell>
</span> <TableCell className="text-center">
{user.auth.is2FAEnabled && ( <Badge
<span className="text-sm text-muted-foreground"> variant={
{user.auth.is2FAEnabled user.isRegistered ? "default" : "secondary"
? "2FA Enabled" }
: "2FA Not Enabled"}
</span>
)}
<div className="flex flex-wrap flex-row gap-3">
{!user.isRegistered && (
<div className="overflow-x-auto flex flex-row gap-4 items-center">
<div className="overflow-x-auto">
<span className="text-sm text-muted-foreground ">
{`${url}/invitation?token=${user.token}`}
</span>
</div>
<button
type="button"
// className="absolute right-2 top-2"
onClick={() => {
copy(`${url}/invitation?token=${user.token}`);
toast.success("Invitation Copied to clipboard");
}}
> >
<CopyIcon className="size-4 text-muted-foreground" /> {user.isRegistered
</button> ? "Registered"
</div> : "Not Registered"}
)} </Badge>
</TableCell>
<TableCell className="text-center">
{user.auth.is2FAEnabled
? "2FA Enabled"
: "2FA Not Enabled"}
</TableCell>
<TableCell className="text-right">
<span className="text-sm text-muted-foreground">
{format(new Date(user.expirationDate), "PPpp")}
</span>
</TableCell>
{user.isRegistered && ( <TableCell className="text-right flex justify-end">
<AddUserPermissions userId={user.userId} /> <DropdownMenu>
)} <DropdownMenuTrigger asChild>
{user.isRegistered && <UpdateUser authId={user.authId} />} <Button variant="ghost" className="h-8 w-8 p-0">
<DeleteUser authId={user.authId} /> <span className="sr-only">Open menu</span>
</div> <MoreHorizontal className="h-4 w-4" />
</div> </Button>
); </DropdownMenuTrigger>
})} <DropdownMenuContent align="end">
<div className="flex flex-col justify-end gap-3 w-full items-end"> <DropdownMenuLabel>Actions</DropdownMenuLabel>
<AddUser /> {!user.isRegistered && (
</div> <DropdownMenuItem
className="w-full cursor-pointer"
onSelect={(e) => {
copy(
`${origin}/invitation?token=${user.token}`,
);
toast.success(
"Invitation Copied to clipboard",
);
}}
>
Copy Invitation
</DropdownMenuItem>
)}
{user.isRegistered && (
<AddUserPermissions userId={user.userId} />
)}
<DeleteUser authId={user.authId} />
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div> </div>
)} )}
</CardContent> </CardContent>

View File

@@ -135,7 +135,7 @@ const Service = (
<TabsTrigger value="domains">Domains</TabsTrigger> <TabsTrigger value="domains">Domains</TabsTrigger>
<TabsTrigger value="advanced">Advanced</TabsTrigger> <TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList> </TabsList>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-2">
<UpdateApplication applicationId={applicationId} /> <UpdateApplication applicationId={applicationId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteApplication applicationId={applicationId} /> <DeleteApplication applicationId={applicationId} />

View File

@@ -116,7 +116,7 @@ const Mariadb = (
<TabsTrigger value="logs">Logs</TabsTrigger> <TabsTrigger value="logs">Logs</TabsTrigger>
<TabsTrigger value="advanced">Advanced</TabsTrigger> <TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList> </TabsList>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-2">
<UpdateMariadb mariadbId={mariadbId} /> <UpdateMariadb mariadbId={mariadbId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteMariadb mariadbId={mariadbId} /> <DeleteMariadb mariadbId={mariadbId} />

View File

@@ -118,7 +118,7 @@ const Mongo = (
<TabsTrigger value="advanced">Advanced</TabsTrigger> <TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList> </TabsList>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-2">
<UpdateMongo mongoId={mongoId} /> <UpdateMongo mongoId={mongoId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteMongo mongoId={mongoId} /> <DeleteMongo mongoId={mongoId} />

View File

@@ -117,7 +117,7 @@ const MySql = (
<TabsTrigger value="advanced">Advanced</TabsTrigger> <TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList> </TabsList>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-2">
<UpdateMysql mysqlId={mysqlId} /> <UpdateMysql mysqlId={mysqlId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteMysql mysqlId={mysqlId} /> <DeleteMysql mysqlId={mysqlId} />

View File

@@ -118,7 +118,7 @@ const Postgresql = (
<TabsTrigger value="advanced">Advanced</TabsTrigger> <TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList> </TabsList>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-2">
<UpdatePostgres postgresId={postgresId} /> <UpdatePostgres postgresId={postgresId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeletePostgres postgresId={postgresId} /> <DeletePostgres postgresId={postgresId} />

View File

@@ -116,7 +116,7 @@ const Redis = (
<TabsTrigger value="advanced">Advanced</TabsTrigger> <TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList> </TabsList>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-2">
<UpdateRedis redisId={redisId} /> <UpdateRedis redisId={redisId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && ( {(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteRedis redisId={redisId} /> <DeleteRedis redisId={redisId} />