mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-09 12:27:48 +00:00
fix: dashboard layout styles (#86)
* fix: dashboard layout scroll * feat: dashboard nav style animation * chore: code input font * style: format code * pref: alert component extraction * fix: global font var not found * fix: code path beak line * chore: remove framer-motion * fix: status color
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -54,3 +54,4 @@ yarn-error.log*
|
|||||||
/.main
|
/.main
|
||||||
|
|
||||||
*.lockb
|
*.lockb
|
||||||
|
*.rdb
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const Login2FA = ({ authId }: Props) => {
|
|||||||
id: authId,
|
id: authId,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Signin succesfully", {
|
toast.success("Signin successfully", {
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ interface Props {
|
|||||||
applicationId: string;
|
applicationId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddRedirectchema = z.object({
|
const AddRedirectSchema = z.object({
|
||||||
command: z.string(),
|
command: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type AddCommand = z.infer<typeof AddRedirectchema>;
|
type AddCommand = z.infer<typeof AddRedirectSchema>;
|
||||||
|
|
||||||
export const AddCommand = ({ applicationId }: Props) => {
|
export const AddCommand = ({ applicationId }: Props) => {
|
||||||
const { data } = api.application.one.useQuery(
|
const { data } = api.application.one.useQuery(
|
||||||
@@ -48,7 +48,7 @@ export const AddCommand = ({ applicationId }: Props) => {
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
command: "",
|
command: "",
|
||||||
},
|
},
|
||||||
resolver: zodResolver(AddRedirectchema),
|
resolver: zodResolver(AddRedirectSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } 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";
|
||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const AddPortchema = z.object({
|
const AddPortSchema = z.object({
|
||||||
publishedPort: z.number().int().min(1).max(65535),
|
publishedPort: z.number().int().min(1).max(65535),
|
||||||
targetPort: z.number().int().min(1).max(65535),
|
targetPort: z.number().int().min(1).max(65535),
|
||||||
protocol: z.enum(["tcp", "udp"], {
|
protocol: z.enum(["tcp", "udp"], {
|
||||||
@@ -41,7 +41,7 @@ const AddPortchema = z.object({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
type AddPort = z.infer<typeof AddPortchema>;
|
type AddPort = z.infer<typeof AddPortSchema>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
@@ -62,7 +62,7 @@ export const AddPort = ({
|
|||||||
publishedPort: 0,
|
publishedPort: 0,
|
||||||
targetPort: 0,
|
targetPort: 0,
|
||||||
},
|
},
|
||||||
resolver: zodResolver(AddPortchema),
|
resolver: zodResolver(AddPortSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -100,14 +100,7 @@ export const AddPort = ({
|
|||||||
Ports are used to expose your application to the internet.
|
Ports are used to expose your application to the internet.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, Pencil } from "lucide-react";
|
import { Pencil } 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";
|
||||||
@@ -106,14 +107,7 @@ export const UpdatePort = ({ portId }: Props) => {
|
|||||||
<DialogTitle>Update</DialogTitle>
|
<DialogTitle>Update</DialogTitle>
|
||||||
<DialogDescription>Update the port</DialogDescription>
|
<DialogDescription>Update the port</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } 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";
|
||||||
@@ -98,14 +98,7 @@ export const AddRedirect = ({
|
|||||||
Redirects are used to redirect requests to another url.
|
Redirects are used to redirect requests to another url.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, Pencil } from "lucide-react";
|
import { Pencil } 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";
|
||||||
@@ -101,14 +102,7 @@ export const UpdateRedirect = ({ redirectId }: Props) => {
|
|||||||
<DialogTitle>Update</DialogTitle>
|
<DialogTitle>Update</DialogTitle>
|
||||||
<DialogDescription>Update the redirect</DialogDescription>
|
<DialogDescription>Update the redirect</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } 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";
|
||||||
@@ -90,14 +90,7 @@ export const AddSecurity = ({
|
|||||||
Add security to your application
|
Add security to your application
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, Pencil } from "lucide-react";
|
import { Pencil } 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";
|
||||||
@@ -96,14 +97,7 @@ export const UpdateSecurity = ({ securityId }: Props) => {
|
|||||||
<DialogTitle>Update</DialogTitle>
|
<DialogTitle>Update</DialogTitle>
|
||||||
<DialogDescription>Update the security</DialogDescription>
|
<DialogDescription>Update the security</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } 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";
|
||||||
@@ -116,14 +116,7 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
|
|||||||
<DialogTitle>Update traefik config</DialogTitle>
|
<DialogTitle>Update traefik config</DialogTitle>
|
||||||
<DialogDescription>Update the traefik config</DialogDescription>
|
<DialogDescription>Update the traefik config</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@@ -140,7 +133,7 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
|
|||||||
<FormLabel>Traefik config</FormLabel>
|
<FormLabel>Traefik config</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-[35rem]"
|
className="h-[35rem] font-mono"
|
||||||
placeholder={`http:
|
placeholder={`http:
|
||||||
routers:
|
routers:
|
||||||
router-name:
|
router-name:
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export const AddVolumes = ({
|
|||||||
<DialogTitle>Volumes / Mounts</DialogTitle>
|
<DialogTitle>Volumes / Mounts</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{/* {isError && (
|
{/* {isError && (
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
<div className="flex items-center flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
<span className="text-sm text-red-600 dark:text-red-400">
|
||||||
{error?.message}
|
{error?.message}
|
||||||
|
|||||||
@@ -27,8 +27,9 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, PlusIcon } from "lucide-react";
|
import { PlusIcon } 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";
|
||||||
@@ -106,14 +107,7 @@ export const AddDomain = ({
|
|||||||
In this section you can add custom domains
|
In this section you can add custom domains
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -27,8 +27,9 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, PenBoxIcon } from "lucide-react";
|
import { PenBoxIcon } 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";
|
||||||
@@ -125,14 +126,7 @@ export const UpdateDomain = ({ domainId }: Props) => {
|
|||||||
In this section you can add custom domains
|
In this section you can add custom domains
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="NODE_ENV=production"
|
placeholder="NODE_ENV=production"
|
||||||
className="h-96"
|
className="h-96 font-mono"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -97,14 +98,7 @@ export const UpdateApplication = ({ applicationId }: Props) => {
|
|||||||
<DialogTitle>Modify Application</DialogTitle>
|
<DialogTitle>Modify Application</DialogTitle>
|
||||||
<DialogDescription>Update the application data</DialogDescription>
|
<DialogDescription>Update the application data</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ export const ShowContainerConfig = ({ containerId }: Props) => {
|
|||||||
View Config
|
View Config
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>
|
<DialogContent className={"w-full md:w-[70vw] max-w-max"}>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Container Config</DialogTitle>
|
<DialogTitle>Container Config</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
See in detail the config of this container
|
See in detail the config of this container
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="text-wrap rounded-lg border p-4 text-sm sm:max-w-[59rem] bg-card">
|
<div className="text-wrap rounded-lg border p-4 text-sm bg-card overflow-y-auto max-h-[80vh]">
|
||||||
<code>
|
<code>
|
||||||
<pre className="whitespace-pre-wrap break-words">
|
<pre className="whitespace-pre-wrap break-words">
|
||||||
{JSON.stringify(data, null, 2)}
|
{JSON.stringify(data, null, 2)}
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ export const DockerLogsId: React.FC<Props> = ({ id, containerId }) => {
|
|||||||
cols: 80,
|
cols: 80,
|
||||||
rows: 30,
|
rows: 30,
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
|
fontWeight: 400,
|
||||||
|
|
||||||
convertEol: true,
|
convertEol: true,
|
||||||
theme: {
|
theme: {
|
||||||
cursor: "transparent",
|
cursor: "transparent",
|
||||||
background: "#19191A",
|
background: "rgba(0, 0, 0, 0)",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,8 +82,8 @@ export const DockerLogsId: React.FC<Props> = ({ id, containerId }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full h-full bg-input rounded-lg p-2 ">
|
<div className="w-full h-full rounded-lg p-2 bg-[#19191A]">
|
||||||
<div id={id} className="rounded-xl" />
|
<div id={id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Terminal } from "@xterm/xterm";
|
|||||||
import { FitAddon } from "xterm-addon-fit";
|
import { FitAddon } from "xterm-addon-fit";
|
||||||
import "@xterm/xterm/css/xterm.css";
|
import "@xterm/xterm/css/xterm.css";
|
||||||
import { AttachAddon } from "@xterm/addon-attach";
|
import { AttachAddon } from "@xterm/addon-attach";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -12,7 +12,7 @@ interface Props {
|
|||||||
|
|
||||||
export const DockerTerminal: React.FC<Props> = ({ id, containerId }) => {
|
export const DockerTerminal: React.FC<Props> = ({ id, containerId }) => {
|
||||||
const termRef = useRef(null);
|
const termRef = useRef(null);
|
||||||
const [activeWay, setActiveWay] = React.useState<string | null>("bash");
|
const [activeWay, setActiveWay] = React.useState<string | undefined>("bash");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const container = document.getElementById(id);
|
const container = document.getElementById(id);
|
||||||
if (container) {
|
if (container) {
|
||||||
@@ -26,7 +26,7 @@ export const DockerTerminal: React.FC<Props> = ({ id, containerId }) => {
|
|||||||
convertEol: true,
|
convertEol: true,
|
||||||
theme: {
|
theme: {
|
||||||
cursor: "transparent",
|
cursor: "transparent",
|
||||||
background: "#19191A",
|
background: "rgba(0, 0, 0, 0)",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const addonFit = new FitAddon();
|
const addonFit = new FitAddon();
|
||||||
@@ -54,14 +54,15 @@ export const DockerTerminal: React.FC<Props> = ({ id, containerId }) => {
|
|||||||
<span>
|
<span>
|
||||||
Select way to connect to <b>{containerId}</b>
|
Select way to connect to <b>{containerId}</b>
|
||||||
</span>
|
</span>
|
||||||
<div className="flex flex-row gap-4 w-fit">
|
<Tabs value={activeWay} onValueChange={setActiveWay}>
|
||||||
<Button onClick={() => setActiveWay("sh")}>SH</Button>
|
<TabsList>
|
||||||
<Button onClick={() => setActiveWay("bash")}>Bash</Button>
|
<TabsTrigger value="bash">Bash</TabsTrigger>
|
||||||
<Button onClick={() => setActiveWay("sh")}>/bin/sh</Button>
|
<TabsTrigger value="sh">/bin/sh</TabsTrigger>
|
||||||
</div>
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-full bg-input rounded-lg p-2 ">
|
<div className="w-full h-full rounded-lg p-2 bg-[#19191A]">
|
||||||
<div id={id} ref={termRef} className="rounded-xl" />
|
<div id={id} ref={termRef} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
import { AlertTriangle } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -86,19 +87,12 @@ export const ShowTraefikFile = ({ path }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="grid w-full py-4 relative"
|
className="grid w-full relative"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<FormField
|
<FormField
|
||||||
@@ -107,10 +101,12 @@ export const ShowTraefikFile = ({ path }: Props) => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="relative">
|
<FormItem className="relative">
|
||||||
<FormLabel>Traefik config</FormLabel>
|
<FormLabel>Traefik config</FormLabel>
|
||||||
<FormDescription>{path}</FormDescription>
|
<FormDescription className="break-all">
|
||||||
|
{path}
|
||||||
|
</FormDescription>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-[35rem]"
|
className="h-[35rem] font-mono"
|
||||||
placeholder={`http:
|
placeholder={`http:
|
||||||
routers:
|
routers:
|
||||||
router-name:
|
router-name:
|
||||||
@@ -128,8 +124,9 @@ routers:
|
|||||||
<pre>
|
<pre>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</pre>
|
</pre>
|
||||||
<div className="flex justify-end absolute z-50 right-6 top-10">
|
<div className="flex justify-end absolute z-50 right-6 top-8">
|
||||||
<Button
|
<Button
|
||||||
|
className="shadow-sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const ShowTraefikSystem = () => {
|
|||||||
const { data: directories } = api.settings.readDirectories.useQuery();
|
const { data: directories } = api.settings.readDirectories.useQuery();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("mt-6 grid gap-4 pb-20")}>
|
<div className={cn("mt-6 md:grid gap-4")}>
|
||||||
<div className="flex flex-col md:flex-row gap-4 md:gap-10 w-full">
|
<div className="flex flex-col md:flex-row gap-4 md:gap-10 w-full">
|
||||||
{directories?.length === 0 && (
|
{directories?.length === 0 && (
|
||||||
<div className="w-full flex-col gap-2 flex items-center justify-center h-[55vh]">
|
<div className="w-full flex-col gap-2 flex items-center justify-center h-[55vh]">
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const ShowMariadbEnvironment = ({ mariadbId }: Props) => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="MARIADB_PASSWORD=1234567678"
|
placeholder="MARIADB_PASSWORD=1234567678"
|
||||||
className="h-96"
|
className="h-96 font-mono"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -97,14 +98,7 @@ export const UpdateMariadb = ({ mariadbId }: Props) => {
|
|||||||
<DialogTitle>Modify MariaDB</DialogTitle>
|
<DialogTitle>Modify MariaDB</DialogTitle>
|
||||||
<DialogDescription>Update the MariaDB data</DialogDescription>
|
<DialogDescription>Update the MariaDB data</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const ShowMongoEnvironment = ({ mongoId }: Props) => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="MONGO_PASSWORD=1234567678"
|
placeholder="MONGO_PASSWORD=1234567678"
|
||||||
className="h-96"
|
className="h-96 font-mono"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -97,14 +98,7 @@ export const UpdateMongo = ({ mongoId }: Props) => {
|
|||||||
<DialogTitle>Modify MongoDB</DialogTitle>
|
<DialogTitle>Modify MongoDB</DialogTitle>
|
||||||
<DialogDescription>Update the MongoDB data</DialogDescription>
|
<DialogDescription>Update the MongoDB data</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const ShowMysqlEnvironment = ({ mysqlId }: Props) => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="MYSQL_PASSWORD=1234567678"
|
placeholder="MYSQL_PASSWORD=1234567678"
|
||||||
className="h-96"
|
className="h-96 font-mono"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -97,14 +98,7 @@ export const UpdateMysql = ({ mysqlId }: Props) => {
|
|||||||
<DialogTitle>Modify MySQL</DialogTitle>
|
<DialogTitle>Modify MySQL</DialogTitle>
|
||||||
<DialogDescription>Update the MySQL data</DialogDescription>
|
<DialogDescription>Update the MySQL data</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const ShowPostgresEnvironment = ({ postgresId }: Props) => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="POSTGRES_PASSWORD=1234567678"
|
placeholder="POSTGRES_PASSWORD=1234567678"
|
||||||
className="h-96"
|
className="h-96 font-mono"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -97,14 +98,7 @@ export const UpdatePostgres = ({ postgresId }: Props) => {
|
|||||||
<DialogTitle>Modify Postgres</DialogTitle>
|
<DialogTitle>Modify Postgres</DialogTitle>
|
||||||
<DialogDescription>Update the Postgres data</DialogDescription>
|
<DialogDescription>Update the Postgres data</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -18,9 +18,10 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, Folder } from "lucide-react";
|
import { Folder } 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";
|
||||||
@@ -93,14 +94,7 @@ export const AddApplication = ({ projectId }: Props) => {
|
|||||||
Assign a name and description to your application
|
Assign a name and description to your application
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export const AddDatabase = ({ projectId }: Props) => {
|
|||||||
<DialogTitle>Databases</DialogTitle>
|
<DialogTitle>Databases</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{/* {isError && (
|
{/* {isError && (
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
<div className="flex items-center flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
<span className="text-sm text-red-600 dark:text-red-400">
|
||||||
{error?.message}
|
{error?.message}
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ import {
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, PlusIcon } from "lucide-react";
|
import { PlusIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@@ -85,14 +86,7 @@ export const AddProject = () => {
|
|||||||
<DialogTitle>Add a project</DialogTitle>
|
<DialogTitle>Add a project</DialogTitle>
|
||||||
<DialogDescription>The home of something big!</DialogDescription>
|
<DialogDescription>The home of something big!</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
id="hook-form-add-project"
|
id="hook-form-add-project"
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ export const ShowProjects = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{data?.length === 0 && (
|
{data?.length === 0 && (
|
||||||
<div className="mt-6 flex h-[50vh] w-full flex-col items-center justify-center ">
|
<div className="mt-6 flex h-[50vh] w-full flex-col items-center justify-center space-y-4">
|
||||||
<FolderInput className="size-10 md:size-28 text-muted" />
|
<FolderInput className="size-10 md:size-28 text-muted-foreground" />
|
||||||
<span className="text-center font-medium text-muted-foreground">
|
<span className="text-center font-medium text-muted-foreground">
|
||||||
No projects added yet. Click on Create project.
|
No projects added yet. Click on Create project.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -101,14 +102,7 @@ export const UpdateProject = ({ projectId }: Props) => {
|
|||||||
Update the details of the project
|
Update the details of the project
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -97,14 +98,7 @@ export const UpdateRedis = ({ redisId }: Props) => {
|
|||||||
<DialogTitle>Modify Redis</DialogTitle>
|
<DialogTitle>Modify Redis</DialogTitle>
|
||||||
<DialogDescription>Update the redis data</DialogDescription>
|
<DialogDescription>Update the redis data</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
import { AlertTriangle } from "lucide-react";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@@ -26,140 +27,135 @@ import { useForm } from "react-hook-form";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const certificateDataHolder = `-----BEGIN CERTIFICATE-----\nMIIFRDCCAyygAwIBAgIUEPOR47ys6VDwMVB9tYoeEka83uQwDQYJKoZIhvcNAQELBQAwGTEXMBUGA1UEAwwObWktZG9taW5pby5jb20wHhcNMjQwMzExMDQyNzU3WhcN\n------END CERTIFICATE-----`;
|
||||||
|
|
||||||
const addCertificate = z.object({
|
const addCertificate = z.object({
|
||||||
name: z.string().min(1, "Name is required"),
|
name: z.string().min(1, "Name is required"),
|
||||||
certificateData: z.string().min(1, "Certificate data is required"),
|
certificateData: z.string().min(1, "Certificate data is required"),
|
||||||
privateKey: z.string().min(1, "Private key is required"),
|
privateKey: z.string().min(1, "Private key is required"),
|
||||||
autoRenew: z.boolean().optional(),
|
autoRenew: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type AddCertificate = z.infer<typeof addCertificate>;
|
type AddCertificate = z.infer<typeof addCertificate>;
|
||||||
|
|
||||||
export const AddCertificate = () => {
|
export const AddCertificate = () => {
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
|
|
||||||
const { mutateAsync, isError, error, isLoading } =
|
const { mutateAsync, isError, error, isLoading } =
|
||||||
api.certificates.create.useMutation();
|
api.certificates.create.useMutation();
|
||||||
|
|
||||||
const form = useForm<AddCertificate>({
|
const form = useForm<AddCertificate>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
certificateData: "",
|
certificateData: "",
|
||||||
privateKey: "",
|
privateKey: "",
|
||||||
autoRenew: false,
|
autoRenew: false,
|
||||||
},
|
},
|
||||||
resolver: zodResolver(addCertificate),
|
resolver: zodResolver(addCertificate),
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.reset();
|
form.reset();
|
||||||
}, [form, form.formState.isSubmitSuccessful, form.reset]);
|
}, [form, form.formState.isSubmitSuccessful, form.reset]);
|
||||||
|
|
||||||
const onSubmit = async (data: AddCertificate) => {
|
const onSubmit = async (data: AddCertificate) => {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
certificateData: data.certificateData,
|
certificateData: data.certificateData,
|
||||||
privateKey: data.privateKey,
|
privateKey: data.privateKey,
|
||||||
autoRenew: data.autoRenew,
|
autoRenew: data.autoRenew,
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Certificate Created");
|
toast.success("Certificate Created");
|
||||||
await utils.certificates.all.invalidate();
|
await utils.certificates.all.invalidate();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error to create the Certificate");
|
toast.error("Error to create the Certificate");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger className="" asChild>
|
<DialogTrigger className="" asChild>
|
||||||
<Button>Add Certificate</Button>
|
<Button>Add Certificate</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>
|
||||||
<DialogTitle>Add Certificate</DialogTitle>
|
<DialogTitle>Add Certificate</DialogTitle>
|
||||||
<DialogDescription>Add a new certificate</DialogDescription>
|
<DialogDescription>Add a new certificate</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
id="hook-form-add-certificate"
|
id="hook-form-add-certificate"
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="grid w-full gap-4 "
|
className="grid w-full gap-4 "
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
return (
|
return (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Certificate Name</FormLabel>
|
<FormLabel>Certificate Name</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={"My Certificate"} {...field} />
|
<Input placeholder={"My Certificate"} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="certificateData"
|
name="certificateData"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<FormLabel>Certificate Data</FormLabel>
|
<FormLabel>Certificate Data</FormLabel>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-32"
|
className="h-32"
|
||||||
placeholder={`-----BEGIN CERTIFICATE-----\nMIIFRDCCAyygAwIBAgIUEPOR47ys6VDwMVB9tYoeEka83uQwDQYJKoZIhvcNAQELBQAwGTEXMBUGA1UEAwwObWktZG9taW5pby5jb20wHhcNMjQwMzExMDQyNzU3WhcN\n------END CERTIFICATE-----`}
|
placeholder={certificateDataHolder}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="privateKey"
|
name="privateKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<FormLabel>Private Key</FormLabel>
|
<FormLabel>Private Key</FormLabel>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-32"
|
className="h-32"
|
||||||
placeholder={`-----BEGIN CERTIFICATE-----\nMIIFRDCCAyygAwIBAgIUEPOR47ys6VDwMVB9tYoeEka83uQwDQYJKoZIhvcNAQELBQAwGTEXMBUGA1UEAwwObWktZG9taW5pby5jb20wHhcNMjQwMzExMDQyNzU3WhcN\n------END CERTIFICATE-----`}
|
placeholder={certificateDataHolder}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<DialogFooter className="flex w-full flex-row !justify-between pt-3">
|
<DialogFooter className="flex w-full flex-row !justify-between pt-3">
|
||||||
<Button
|
<Button
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
form="hook-form-add-certificate"
|
form="hook-form-add-certificate"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</Form>
|
</Form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } 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";
|
||||||
@@ -87,14 +87,7 @@ export const AddDestination = () => {
|
|||||||
In this section you can add destinations for your backups.
|
In this section you can add destinations for your backups.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, PenBoxIcon } from "lucide-react";
|
import { PenBoxIcon } 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";
|
||||||
@@ -111,14 +112,7 @@ export const UpdateDestination = ({ destinationId }: Props) => {
|
|||||||
Update the current destination config
|
Update the current destination config
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ import {
|
|||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { extractServices } from "@/pages/dashboard/project/[projectId]";
|
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 { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, ListTodo } from "lucide-react";
|
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";
|
||||||
@@ -115,14 +116,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
|
|||||||
<DialogTitle>Permissions</DialogTitle>
|
<DialogTitle>Permissions</DialogTitle>
|
||||||
<DialogDescription>Add or remove permissions</DialogDescription>
|
<DialogDescription>Add or remove permissions</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } 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";
|
||||||
@@ -73,14 +73,7 @@ export const AddUser = () => {
|
|||||||
<DialogTitle>Add User</DialogTitle>
|
<DialogTitle>Add User</DialogTitle>
|
||||||
<DialogDescription>Invite a new user</DialogDescription>
|
<DialogDescription>Invite a new user</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const ShowUsers = () => {
|
|||||||
key={user.userId}
|
key={user.userId}
|
||||||
className="flex gap-2 flex-col justify-start border p-4 rounded-lg"
|
className="flex gap-2 flex-col justify-start border p-4 rounded-lg"
|
||||||
>
|
>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-foreground">
|
||||||
{user.auth.email}
|
{user.auth.email}
|
||||||
</span>
|
</span>
|
||||||
{!user.isRegistered && (
|
{!user.isRegistered && (
|
||||||
|
|||||||
@@ -18,11 +18,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { AlertTriangle, SquarePen } from "lucide-react";
|
import { SquarePen } from "lucide-react";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
const updateUserSchema = z.object({
|
const updateUserSchema = z.object({
|
||||||
@@ -100,14 +101,7 @@ export const UpdateUser = ({ authId }: Props) => {
|
|||||||
<DialogTitle>Update User</DialogTitle>
|
<DialogTitle>Update User</DialogTitle>
|
||||||
<DialogDescription>Update the user</DialogDescription>
|
<DialogDescription>Update the user</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid items-center gap-4">
|
<div className="grid items-center gap-4">
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -89,14 +89,7 @@ export const ShowMainTraefikConfig = ({ children }: Props) => {
|
|||||||
<DialogTitle>Update traefik config</DialogTitle>
|
<DialogTitle>Update traefik config</DialogTitle>
|
||||||
<DialogDescription>Update the traefik config</DialogDescription>
|
<DialogDescription>Update the traefik config</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@@ -113,7 +106,7 @@ export const ShowMainTraefikConfig = ({ children }: Props) => {
|
|||||||
<FormLabel>Traefik config</FormLabel>
|
<FormLabel>Traefik config</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-[35rem]"
|
className="h-[35rem] font-mono"
|
||||||
placeholder={`providers:
|
placeholder={`providers:
|
||||||
docker:
|
docker:
|
||||||
defaultRule: 'Host('dokploy.com')'
|
defaultRule: 'Host('dokploy.com')'
|
||||||
@@ -136,8 +129,10 @@ export const ShowMainTraefikConfig = ({ children }: Props) => {
|
|||||||
<pre>
|
<pre>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</pre>
|
</pre>
|
||||||
<div className="flex justify-end absolute z-50 right-6 top-10">
|
<div className="flex justify-end absolute z-50 right-6 top-0">
|
||||||
<Button
|
<Button
|
||||||
|
className="shadow-sm"
|
||||||
|
variant="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setCanEdit(!canEdit);
|
setCanEdit(!canEdit);
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -92,14 +92,7 @@ export const ShowServerMiddlewareConfig = ({ children }: Props) => {
|
|||||||
<DialogTitle>Update Middleware config</DialogTitle>
|
<DialogTitle>Update Middleware config</DialogTitle>
|
||||||
<DialogDescription>Update the middleware config</DialogDescription>
|
<DialogDescription>Update the middleware config</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@@ -116,7 +109,7 @@ export const ShowServerMiddlewareConfig = ({ children }: Props) => {
|
|||||||
<FormLabel>Traefik config</FormLabel>
|
<FormLabel>Traefik config</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-[35rem]"
|
className="h-[35rem] font-mono"
|
||||||
placeholder={`http:
|
placeholder={`http:
|
||||||
routers:
|
routers:
|
||||||
router-name:
|
router-name:
|
||||||
@@ -134,8 +127,10 @@ routers:
|
|||||||
<pre>
|
<pre>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</pre>
|
</pre>
|
||||||
<div className="flex justify-end absolute z-50 right-6 top-10">
|
<div className="flex justify-end absolute z-50 right-6 top-0">
|
||||||
<Button
|
<Button
|
||||||
|
className="shadow-sm"
|
||||||
|
variant="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setCanEdit(!canEdit);
|
setCanEdit(!canEdit);
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -92,14 +92,7 @@ export const ShowServerTraefikConfig = ({ children }: Props) => {
|
|||||||
<DialogTitle>Update traefik config</DialogTitle>
|
<DialogTitle>Update traefik config</DialogTitle>
|
||||||
<DialogDescription>Update the traefik config</DialogDescription>
|
<DialogDescription>Update the traefik config</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{isError && (
|
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
|
|
||||||
<AlertTriangle className="text-red-600 dark:text-red-400" />
|
|
||||||
<span className="text-sm text-red-600 dark:text-red-400">
|
|
||||||
{error?.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@@ -116,7 +109,7 @@ export const ShowServerTraefikConfig = ({ children }: Props) => {
|
|||||||
<FormLabel>Traefik config</FormLabel>
|
<FormLabel>Traefik config</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-[35rem]"
|
className="h-[35rem] font-mono"
|
||||||
placeholder={`http:
|
placeholder={`http:
|
||||||
routers:
|
routers:
|
||||||
router-name:
|
router-name:
|
||||||
@@ -134,8 +127,10 @@ routers:
|
|||||||
<pre>
|
<pre>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</pre>
|
</pre>
|
||||||
<div className="flex justify-end absolute z-50 right-6 top-10">
|
<div className="flex justify-end absolute z-50 right-6 top-0">
|
||||||
<Button
|
<Button
|
||||||
|
className="shadow-sm"
|
||||||
|
variant="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setCanEdit(!canEdit);
|
setCanEdit(!canEdit);
|
||||||
|
|||||||
@@ -10,19 +10,15 @@ export const DashboardLayout = ({ children, tab }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="bg-radial relative flex flex-col bg-background pt-6"
|
className="bg-radial relative flex flex-col bg-background min-h-screen w-full"
|
||||||
id="app-container"
|
id="app-container"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-center">
|
<Navbar />
|
||||||
<div className="w-full">
|
<main className="pt-6 flex w-full flex-col items-center">
|
||||||
<Navbar />
|
<div className="w-full max-w-8xl px-4 lg:px-8">
|
||||||
<main className="mt-6 flex w-full flex-col items-center">
|
<NavigationTabs tab={tab}>{children}</NavigationTabs>
|
||||||
<div className="w-full max-w-8xl px-4 lg:px-8">
|
|
||||||
<NavigationTabs tab={tab}>{children}</NavigationTabs>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const Navbar = () => {
|
|||||||
const { mutateAsync } = api.auth.logout.useMutation();
|
const { mutateAsync } = api.auth.logout.useMutation();
|
||||||
return (
|
return (
|
||||||
<nav className="border-divider sticky inset-x-0 top-0 z-40 flex h-auto w-full items-center justify-center border-b bg-background/70 backdrop-blur-lg backdrop-saturate-150 data-[menu-open=true]:border-none data-[menu-open=true]:backdrop-blur-xl">
|
<nav className="border-divider sticky inset-x-0 top-0 z-40 flex h-auto w-full items-center justify-center border-b bg-background/70 backdrop-blur-lg backdrop-saturate-150 data-[menu-open=true]:border-none data-[menu-open=true]:backdrop-blur-xl">
|
||||||
<header className="relative z-40 flex h-[var(--navbar-height)] w-full max-w-8xl flex-row flex-nowrap items-center justify-between gap-4 px-4 sm:px-6">
|
<header className="relative z-40 flex w-full max-w-8xl flex-row flex-nowrap items-center justify-between gap-4 px-4 sm:px-6 h-16">
|
||||||
<div className="text-medium box-border flex flex-grow basis-0 flex-row flex-nowrap items-center justify-start whitespace-nowrap bg-transparent no-underline">
|
<div className="text-medium box-border flex flex-grow basis-0 flex-row flex-nowrap items-center justify-start whitespace-nowrap bg-transparent no-underline">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/projects"
|
href="/dashboard/projects"
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
import { AddProject } from "@/components/dashboard/projects/add";
|
import { AddProject } from "@/components/dashboard/projects/add";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import type { Auth } from "@/server/api/services/auth";
|
||||||
|
import type { User } from "@/server/api/services/user";
|
||||||
|
|
||||||
|
interface TabInfo {
|
||||||
|
label: string;
|
||||||
|
tabLabel?: string;
|
||||||
|
description: string;
|
||||||
|
index: string;
|
||||||
|
isShow?: ({ rol, user }: { rol?: Auth["rol"]; user?: User }) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type TabState =
|
export type TabState =
|
||||||
| "projects"
|
| "projects"
|
||||||
@@ -11,6 +21,41 @@ export type TabState =
|
|||||||
| "traefik"
|
| "traefik"
|
||||||
| "docker";
|
| "docker";
|
||||||
|
|
||||||
|
const tabMap: Record<TabState, TabInfo> = {
|
||||||
|
projects: {
|
||||||
|
label: "Projects",
|
||||||
|
description: "Manage your projects",
|
||||||
|
index: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
monitoring: {
|
||||||
|
label: "Monitoring",
|
||||||
|
description: "Monitor your projects",
|
||||||
|
index: "/dashboard/monitoring",
|
||||||
|
},
|
||||||
|
traefik: {
|
||||||
|
label: "Traefik",
|
||||||
|
tabLabel: "Traefik File System",
|
||||||
|
description: "Manage your traefik",
|
||||||
|
index: "/dashboard/traefik",
|
||||||
|
isShow: ({ rol, user }) => {
|
||||||
|
return Boolean(rol === "admin" || user?.canAccessToTraefikFiles);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docker: {
|
||||||
|
label: "Docker",
|
||||||
|
description: "Manage your docker",
|
||||||
|
index: "/dashboard/docker",
|
||||||
|
isShow: ({ rol, user }) => {
|
||||||
|
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
label: "Settings",
|
||||||
|
description: "Manage your settings",
|
||||||
|
index: "/dashboard/settings/server",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tab: TabState;
|
tab: TabState;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -34,23 +79,19 @@ export const NavigationTabs = ({ tab, children }: Props) => {
|
|||||||
setActiveTab(tab);
|
setActiveTab(tab);
|
||||||
}, [tab]);
|
}, [tab]);
|
||||||
|
|
||||||
|
const activeTabInfo = useMemo(() => {
|
||||||
|
return tabMap[activeTab];
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="gap-12 min-h-screen">
|
<div className="gap-12">
|
||||||
<header className="mb-6 flex w-full items-center gap-2 justify-between flex-wrap">
|
<header className="mb-6 flex w-full items-center gap-2 justify-between flex-wrap">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-xl font-bold lg:text-3xl">
|
<h1 className="text-xl font-bold lg:text-3xl">
|
||||||
{tab === "projects" && "Projects"}
|
{activeTabInfo.label}
|
||||||
{tab === "monitoring" && "Monitoring"}
|
|
||||||
{tab === "settings" && "Settings"}
|
|
||||||
{tab === "traefik" && "Traefik"}
|
|
||||||
{tab === "docker" && "Docker"}
|
|
||||||
</h1>
|
</h1>
|
||||||
<p className="lg:text-medium text-muted-foreground">
|
<p className="lg:text-medium text-muted-foreground">
|
||||||
{tab === "projects" && "Manage your deployments"}
|
{activeTabInfo.description}
|
||||||
{tab === "monitoring" && "Watch the usage of your server"}
|
|
||||||
{tab === "settings" && "Check the configuration"}
|
|
||||||
{tab === "traefik" && "Read the traefik config and update it"}
|
|
||||||
{tab === "docker" && "Manage the docker containers"}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{tab === "projects" &&
|
{tab === "projects" &&
|
||||||
@@ -60,54 +101,36 @@ export const NavigationTabs = ({ tab, children }: Props) => {
|
|||||||
<Tabs
|
<Tabs
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onValueChange={(e) => {
|
onValueChange={async (e) => {
|
||||||
if (e === "settings") {
|
|
||||||
router.push("/dashboard/settings/server");
|
|
||||||
} else {
|
|
||||||
router.push(`/dashboard/${e}`);
|
|
||||||
}
|
|
||||||
setActiveTab(e as TabState);
|
setActiveTab(e as TabState);
|
||||||
|
router.push(tabMap[e as TabState].index);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* className="grid w-fit grid-cols-4 bg-transparent" */}
|
{/* className="grid w-fit grid-cols-4 bg-transparent" */}
|
||||||
<div className="flex flex-row items-center justify-between w-full gap-4 max-sm:overflow-x-auto">
|
<div className="flex flex-row items-center justify-between w-full gap-4 max-sm:overflow-x-auto border-b border-b-divider pb-1">
|
||||||
<TabsList className="md:grid md:w-fit md:grid-cols-5 justify-start bg-transparent">
|
<TabsList className="bg-transparent relative px-0">
|
||||||
<TabsTrigger
|
{Object.keys(tabMap).map((key) => {
|
||||||
value="projects"
|
const tab = tabMap[key as TabState];
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
if (tab.isShow && !tab.isShow?.({ rol: data?.rol, user })) {
|
||||||
>
|
return null;
|
||||||
Projects
|
}
|
||||||
</TabsTrigger>
|
return (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="monitoring"
|
key={key}
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
value={key}
|
||||||
>
|
className="relative py-2.5 md:px-5 data-[state=active]:shadow-none data-[state=active]:bg-transparent rounded-md hover:bg-zinc-100 hover:dark:bg-zinc-800 data-[state=active]:hover:bg-zinc-100 data-[state=active]:hover:dark:bg-zinc-800"
|
||||||
Monitoring
|
>
|
||||||
</TabsTrigger>
|
<span className="relative z-[1] w-full">
|
||||||
|
{tab.tabLabel || tab.label}
|
||||||
{(data?.rol === "admin" || user?.canAccessToTraefikFiles) && (
|
</span>
|
||||||
<TabsTrigger
|
{key === activeTab && (
|
||||||
value="traefik"
|
<div className="absolute -bottom-[5.5px] w-full">
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
<div className="h-0.5 bg-foreground rounded-t-md" />
|
||||||
>
|
</div>
|
||||||
Traefik File System
|
)}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
)}
|
);
|
||||||
{(data?.rol === "admin" || user?.canAccessToDocker) && (
|
})}
|
||||||
<TabsTrigger
|
|
||||||
value="docker"
|
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
|
||||||
>
|
|
||||||
Docker
|
|
||||||
</TabsTrigger>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<TabsTrigger
|
|
||||||
value="settings"
|
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
|
||||||
>
|
|
||||||
Settings
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
51
components/shared/alert-block.tsx
Normal file
51
components/shared/alert-block.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
interface Props extends React.ComponentPropsWithoutRef<"div"> {
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
type?: "info" | "success" | "warning" | "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { AlertTriangle, AlertCircle, CheckCircle2, Info } from "lucide-react";
|
||||||
|
|
||||||
|
const iconMap = {
|
||||||
|
info: {
|
||||||
|
className: "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400",
|
||||||
|
icon: Info,
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
className:
|
||||||
|
"bg-green-50 dark:bg-green-950 text-green-600 dark:text-green-400",
|
||||||
|
icon: CheckCircle2,
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
className:
|
||||||
|
"bg-orange-50 dark:bg-orange-950 text-orange-600 dark:text-orange-400",
|
||||||
|
icon: AlertCircle,
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
className: "bg-red-50 dark:bg-red-950 text-red-600 dark:text-red-400",
|
||||||
|
icon: AlertTriangle,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function AlertBlock({
|
||||||
|
type = "info",
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: Props) {
|
||||||
|
const { className: iconClassName, icon: Icon } = iconMap[type];
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center flex-row gap-4 rounded-lg p-2",
|
||||||
|
iconClassName,
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{icon || <Icon className="text-current" />}
|
||||||
|
<span className="text-sm text-current">{children}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -17,21 +17,21 @@ export const StatusTooltip = ({ status, className }: Props) => {
|
|||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
{status === "idle" && (
|
{status === "idle" && (
|
||||||
<div className={cn(" size-3.5 rounded-full bg-card", className)} />
|
<div className={cn("size-3.5 rounded-full bg-muted-foreground dark:bg-card", className)} />
|
||||||
)}
|
)}
|
||||||
{status === "error" && (
|
{status === "error" && (
|
||||||
<div
|
<div
|
||||||
className={cn(" size-3.5 rounded-full bg-destructive", className)}
|
className={cn("size-3.5 rounded-full bg-destructive", className)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{status === "done" && (
|
{status === "done" && (
|
||||||
<div
|
<div
|
||||||
className={cn(" size-3.5 rounded-full bg-primary", className)}
|
className={cn("size-3.5 rounded-full bg-primary", className)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{status === "running" && (
|
{status === "running" && (
|
||||||
<div
|
<div
|
||||||
className={cn(" size-3.5 rounded-full bg-yellow-500", className)}
|
className={cn("size-3.5 rounded-full bg-yellow-500", className)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|||||||
59
components/ui/alert.tsx
Normal file
59
components/ui/alert.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const alertVariants = cva(
|
||||||
|
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-background text-foreground",
|
||||||
|
destructive:
|
||||||
|
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Alert = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="alert"
|
||||||
|
className={cn(alertVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Alert.displayName = "Alert"
|
||||||
|
|
||||||
|
const AlertTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h5
|
||||||
|
ref={ref}
|
||||||
|
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertTitle.displayName = "AlertTitle"
|
||||||
|
|
||||||
|
const AlertDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDescription.displayName = "AlertDescription"
|
||||||
|
|
||||||
|
export { Alert, AlertTitle, AlertDescription }
|
||||||
@@ -165,7 +165,7 @@ const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="text-sm truncate">{item.name}</span>
|
<span className="text-sm truncate font-mono">{item.name}</span>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="pl-6">
|
<AccordionContent className="pl-6">
|
||||||
{item.children.length === 0 && (
|
{item.children.length === 0 && (
|
||||||
@@ -244,7 +244,7 @@ const Leaf = React.forwardRef<
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<p className=" text-sm whitespace-normal">{item.name}</p>
|
<p className=" text-sm whitespace-normal font-mono">{item.name}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -103,7 +103,6 @@
|
|||||||
"zod": "^3.23.4"
|
"zod": "^3.23.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"xterm-readline": "1.1.1",
|
|
||||||
"@biomejs/biome": "1.7.1",
|
"@biomejs/biome": "1.7.1",
|
||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
"@types/dockerode": "3.3.23",
|
"@types/dockerode": "3.3.23",
|
||||||
@@ -125,7 +124,8 @@
|
|||||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"tsx": "^4.7.0",
|
"tsx": "^4.7.0",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2",
|
||||||
|
"xterm-readline": "1.1.1"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
"initVersion": "7.25.2"
|
"initVersion": "7.25.2"
|
||||||
|
|||||||
@@ -1,44 +1,53 @@
|
|||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import "@/styles/globals.css";
|
import "@/styles/globals.css";
|
||||||
|
|
||||||
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import type { ReactElement, ReactNode } from "react";
|
import type { ReactElement, ReactNode } from "react";
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||||
getLayout?: (page: ReactElement) => ReactNode;
|
getLayout?: (page: ReactElement) => ReactNode;
|
||||||
// session: Session | null;
|
// session: Session | null;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AppPropsWithLayout = AppProps & {
|
type AppPropsWithLayout = AppProps & {
|
||||||
Component: NextPageWithLayout;
|
Component: NextPageWithLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MyApp = ({
|
const MyApp = ({
|
||||||
Component,
|
Component,
|
||||||
pageProps: { ...pageProps },
|
pageProps: { ...pageProps },
|
||||||
}: AppPropsWithLayout) => {
|
}: AppPropsWithLayout) => {
|
||||||
const getLayout = Component.getLayout ?? ((page) => page);
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<style jsx global>{`
|
||||||
<title>Dokploy</title>
|
:root {
|
||||||
</Head>
|
--font-inter: ${inter.style.fontFamily};
|
||||||
<ThemeProvider
|
}
|
||||||
attribute="class"
|
`}</style>
|
||||||
defaultTheme="system"
|
<Head>
|
||||||
enableSystem
|
<title>Dokploy</title>
|
||||||
disableTransitionOnChange
|
</Head>
|
||||||
forcedTheme={Component.theme}
|
<ThemeProvider
|
||||||
>
|
attribute="class"
|
||||||
<Toaster richColors />
|
defaultTheme="system"
|
||||||
{getLayout(<Component {...pageProps} />)}
|
enableSystem
|
||||||
</ThemeProvider>
|
disableTransitionOnChange
|
||||||
</>
|
forcedTheme={Component.theme}
|
||||||
);
|
>
|
||||||
|
<Toaster richColors />
|
||||||
|
{getLayout(<Component {...pageProps} />)}
|
||||||
|
</ThemeProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default api.withTRPC(MyApp);
|
export default api.withTRPC(MyApp);
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
import { Head, Html, Main, NextScript } from "next/document";
|
import { Head, Html, Main, NextScript } from "next/document";
|
||||||
|
|
||||||
export default function Document() {
|
export default function Document() {
|
||||||
return (
|
return (
|
||||||
<Html lang="en">
|
<Html lang="en" className="font-sans">
|
||||||
<Head>
|
<Head />
|
||||||
<link rel="shortcut icon" href="/icon.svg" />
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@100;400;500;600;700&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
<body className="flex h-full flex-col ">
|
<body className="flex h-full flex-col font-sans">
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
*,
|
|
||||||
html {
|
|
||||||
font-family: "Inter", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Config } from "tailwindcss";
|
import type { Config } from "tailwindcss";
|
||||||
|
import defaultTheme from "tailwindcss/defaultTheme";
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
@@ -18,6 +19,9 @@ const config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ["var(--font-inter)", ...defaultTheme.fontFamily.sans],
|
||||||
|
},
|
||||||
maxWidth: {
|
maxWidth: {
|
||||||
"2xl": "40rem",
|
"2xl": "40rem",
|
||||||
"8xl": "85rem",
|
"8xl": "85rem",
|
||||||
|
|||||||
Reference in New Issue
Block a user