mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
Add scrolling to organization picker dropdown
Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import { z } from "zod";
|
|||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { CodeEditor } from "@/components/shared/code-editor";
|
import { CodeEditor } from "@/components/shared/code-editor";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -24,7 +25,6 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { badgeStateColor } from "@/components/dashboard/application/logs/show";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { badgeStateColor } from "@/components/dashboard/application/logs/show";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormDescription,
|
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormDescription,
|
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormDescription,
|
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormDescription,
|
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
@@ -24,6 +23,7 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
|
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { ArrowRightLeft, Plus, Trash2 } from "lucide-react";
|
import { ArrowRightLeft, Plus, Trash2 } from "lucide-react";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useFieldArray, useForm } from "react-hook-form";
|
import { useFieldArray, useForm } from "react-hook-form";
|
||||||
@@ -36,6 +35,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -638,127 +638,129 @@ function SidebarLogo() {
|
|||||||
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
||||||
Organizations
|
Organizations
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
{organizations?.map((org) => {
|
<div className="max-h-[60vh] overflow-y-auto">
|
||||||
const isDefault = org.members?.[0]?.isDefault ?? false;
|
{organizations?.map((org) => {
|
||||||
return (
|
const isDefault = org.members?.[0]?.isDefault ?? false;
|
||||||
<div
|
return (
|
||||||
className="flex flex-row justify-between"
|
<div
|
||||||
key={org.name}
|
className="flex flex-row justify-between"
|
||||||
>
|
key={org.name}
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={async () => {
|
|
||||||
await authClient.organization.setActive({
|
|
||||||
organizationId: org.id,
|
|
||||||
});
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
className="w-full gap-2 p-2"
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-1">
|
<DropdownMenuItem
|
||||||
<div className="flex items-center gap-2">
|
onClick={async () => {
|
||||||
{org.name}
|
await authClient.organization.setActive({
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex size-6 items-center justify-center rounded-sm border">
|
|
||||||
<Logo
|
|
||||||
className={cn(
|
|
||||||
"transition-all",
|
|
||||||
state === "collapsed" ? "size-6" : "size-10",
|
|
||||||
)}
|
|
||||||
logoUrl={org.logo ?? undefined}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className={cn(
|
|
||||||
"group",
|
|
||||||
isDefault
|
|
||||||
? "hover:bg-yellow-500/10"
|
|
||||||
: "hover:bg-blue-500/10",
|
|
||||||
)}
|
|
||||||
isLoading={isSettingDefault && !isDefault}
|
|
||||||
disabled={isDefault}
|
|
||||||
onClick={async (e) => {
|
|
||||||
if (isDefault) return;
|
|
||||||
e.stopPropagation();
|
|
||||||
await setDefaultOrganization({
|
|
||||||
organizationId: org.id,
|
organizationId: org.id,
|
||||||
})
|
});
|
||||||
.then(() => {
|
window.location.reload();
|
||||||
refetch();
|
|
||||||
toast.success("Default organization updated");
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
toast.error(
|
|
||||||
error?.message ||
|
|
||||||
"Error setting default organization",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
title={
|
className="w-full gap-2 p-2"
|
||||||
isDefault
|
|
||||||
? "Default organization"
|
|
||||||
: "Set as default"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{isDefault ? (
|
<div className="flex flex-col gap-1">
|
||||||
<Star
|
<div className="flex items-center gap-2">
|
||||||
fill="#eab308"
|
{org.name}
|
||||||
stroke="#eab308"
|
</div>
|
||||||
className="size-4 text-yellow-500"
|
</div>
|
||||||
|
<div className="flex size-6 items-center justify-center rounded-sm border">
|
||||||
|
<Logo
|
||||||
|
className={cn(
|
||||||
|
"transition-all",
|
||||||
|
state === "collapsed" ? "size-6" : "size-10",
|
||||||
|
)}
|
||||||
|
logoUrl={org.logo ?? undefined}
|
||||||
/>
|
/>
|
||||||
) : (
|
</div>
|
||||||
<Star
|
</DropdownMenuItem>
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
<div className="flex items-center gap-2">
|
||||||
className="size-4 text-gray-400 group-hover:text-blue-500 transition-colors"
|
<Button
|
||||||
/>
|
variant="ghost"
|
||||||
)}
|
size="icon"
|
||||||
</Button>
|
className={cn(
|
||||||
{org.ownerId === session?.user?.id && (
|
"group",
|
||||||
<>
|
isDefault
|
||||||
<AddOrganization organizationId={org.id} />
|
? "hover:bg-yellow-500/10"
|
||||||
<DialogAction
|
: "hover:bg-blue-500/10",
|
||||||
title="Delete Organization"
|
)}
|
||||||
description="Are you sure you want to delete this organization?"
|
isLoading={isSettingDefault && !isDefault}
|
||||||
type="destructive"
|
disabled={isDefault}
|
||||||
onClick={async () => {
|
onClick={async (e) => {
|
||||||
await deleteOrganization({
|
if (isDefault) return;
|
||||||
organizationId: org.id,
|
e.stopPropagation();
|
||||||
|
await setDefaultOrganization({
|
||||||
|
organizationId: org.id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
refetch();
|
||||||
|
toast.success("Default organization updated");
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch((error) => {
|
||||||
refetch();
|
toast.error(
|
||||||
toast.success(
|
error?.message ||
|
||||||
"Organization deleted successfully",
|
"Error setting default organization",
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
isDefault
|
||||||
|
? "Default organization"
|
||||||
|
: "Set as default"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isDefault ? (
|
||||||
|
<Star
|
||||||
|
fill="#eab308"
|
||||||
|
stroke="#eab308"
|
||||||
|
className="size-4 text-yellow-500"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Star
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
className="size-4 text-gray-400 group-hover:text-blue-500 transition-colors"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
{org.ownerId === session?.user?.id && (
|
||||||
|
<>
|
||||||
|
<AddOrganization organizationId={org.id} />
|
||||||
|
<DialogAction
|
||||||
|
title="Delete Organization"
|
||||||
|
description="Are you sure you want to delete this organization?"
|
||||||
|
type="destructive"
|
||||||
|
onClick={async () => {
|
||||||
|
await deleteOrganization({
|
||||||
|
organizationId: org.id,
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.then(() => {
|
||||||
toast.error(
|
refetch();
|
||||||
error?.message ||
|
toast.success(
|
||||||
"Error deleting organization",
|
"Organization deleted successfully",
|
||||||
);
|
);
|
||||||
});
|
})
|
||||||
}}
|
.catch((error) => {
|
||||||
>
|
toast.error(
|
||||||
<Button
|
error?.message ||
|
||||||
variant="ghost"
|
"Error deleting organization",
|
||||||
size="icon"
|
);
|
||||||
className="group hover:bg-red-500/10"
|
});
|
||||||
isLoading={isRemoving}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
|
<Button
|
||||||
</Button>
|
variant="ghost"
|
||||||
</DialogAction>
|
size="icon"
|
||||||
</>
|
className="group hover:bg-red-500/10"
|
||||||
)}
|
isLoading={isRemoving}
|
||||||
|
>
|
||||||
|
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</div>
|
||||||
{(user?.role === "owner" ||
|
{(user?.role === "owner" ||
|
||||||
user?.role === "admin" ||
|
user?.role === "admin" ||
|
||||||
isCloud) && (
|
isCloud) && (
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { authClient } from "@/lib/auth-client";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { authClient } from "@/lib/auth-client";
|
||||||
|
|
||||||
export function SignInWithGithub() {
|
export function SignInWithGithub() {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { authClient } from "@/lib/auth-client";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { authClient } from "@/lib/auth-client";
|
||||||
|
|
||||||
export function SignInWithGoogle() {
|
export function SignInWithGoogle() {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import { mountRouter } from "./routers/mount";
|
|||||||
import { mysqlRouter } from "./routers/mysql";
|
import { mysqlRouter } from "./routers/mysql";
|
||||||
import { notificationRouter } from "./routers/notification";
|
import { notificationRouter } from "./routers/notification";
|
||||||
import { organizationRouter } from "./routers/organization";
|
import { organizationRouter } from "./routers/organization";
|
||||||
import { licenseKeyRouter } from "./routers/proprietary/license-key";
|
|
||||||
import { ssoRouter } from "./routers/proprietary/sso";
|
|
||||||
import { portRouter } from "./routers/port";
|
import { portRouter } from "./routers/port";
|
||||||
import { postgresRouter } from "./routers/postgres";
|
import { postgresRouter } from "./routers/postgres";
|
||||||
import { previewDeploymentRouter } from "./routers/preview-deployment";
|
import { previewDeploymentRouter } from "./routers/preview-deployment";
|
||||||
import { projectRouter } from "./routers/project";
|
import { projectRouter } from "./routers/project";
|
||||||
|
import { licenseKeyRouter } from "./routers/proprietary/license-key";
|
||||||
|
import { ssoRouter } from "./routers/proprietary/sso";
|
||||||
import { redirectsRouter } from "./routers/redirects";
|
import { redirectsRouter } from "./routers/redirects";
|
||||||
import { redisRouter } from "./routers/redis";
|
import { redisRouter } from "./routers/redis";
|
||||||
import { registryRouter } from "./routers/registry";
|
import { registryRouter } from "./routers/registry";
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { exit } from "node:process";
|
|
||||||
import { exec } from "node:child_process";
|
import { exec } from "node:child_process";
|
||||||
|
import { exit } from "node:process";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
import { setupDirectories } from "@dokploy/server/setup/config-paths";
|
import { setupDirectories } from "@dokploy/server/setup/config-paths";
|
||||||
import { initializePostgres } from "@dokploy/server/setup/postgres-setup";
|
import { initializePostgres } from "@dokploy/server/setup/postgres-setup";
|
||||||
import { initializeRedis } from "@dokploy/server/setup/redis-setup";
|
import { initializeRedis } from "@dokploy/server/setup/redis-setup";
|
||||||
|
|||||||
Reference in New Issue
Block a user