diff --git a/.claude/skills/frontend-design/SKILL.md b/.claude/skills/frontend-design/SKILL.md new file mode 100644 index 000000000..600b6db41 --- /dev/null +++ b/.claude/skills/frontend-design/SKILL.md @@ -0,0 +1,42 @@ +--- +name: frontend-design +description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics. +license: Complete terms in LICENSE.txt +--- + +This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices. + +The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints. + +## Design Thinking + +Before coding, understand the context and commit to a BOLD aesthetic direction: +- **Purpose**: What problem does this interface solve? Who uses it? +- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction. +- **Constraints**: Technical requirements (framework, performance, accessibility). +- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember? + +**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity. + +Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is: +- Production-grade and functional +- Visually striking and memorable +- Cohesive with a clear aesthetic point-of-view +- Meticulously refined in every detail + +## Frontend Aesthetics Guidelines + +Focus on: +- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font. +- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. +- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. +- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density. +- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays. + +NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character. + +Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations. + +**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well. + +Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision. \ No newline at end of file diff --git a/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx b/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx index 94a5c72a6..196a9271d 100644 --- a/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx +++ b/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx @@ -1,4 +1,11 @@ -import { FileIcon, Folder, Loader2, Workflow } from "lucide-react"; +import { + FileIcon, + Folder, + FolderOpen, + Loader2, + MousePointerClick, + Workflow, +} from "lucide-react"; import React from "react"; import { AlertBlock } from "@/components/shared/alert-block"; import { @@ -68,12 +75,22 @@ export const ShowTraefikSystem = ({ serverId }: Props) => { )} {directories?.length === 0 && ( -
- - No directories or files detected in{" "} - {"'/etc/dokploy/traefik'"} - - +
+
+ +
+
+ + No configuration files found + + + There are no directories or files in{" "} + + /etc/dokploy/traefik + {" "} + on this server yet. + +
)} {directories && directories?.length > 0 && ( @@ -89,11 +106,19 @@ export const ShowTraefikSystem = ({ serverId }: Props) => { {file ? ( ) : ( -
- - No file selected - - +
+
+ +
+
+ + Select a file to edit + + + Choose a file from the tree on the left to view + and edit its contents. + +
)}
diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx deleted file mode 100644 index 3056815cf..000000000 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useState } from "react"; -import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { ShowNodes } from "./show-nodes"; - -interface Props { - serverId: string; -} - -export const ShowNodesModal = ({ serverId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - - return ( - - - e.preventDefault()} - > - Show Swarm Nodes - - - -
- -
-
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx deleted file mode 100644 index e040de11d..000000000 --- a/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useState } from "react"; -import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { ShowContainers } from "../../docker/show/show-containers"; - -interface Props { - serverId: string; -} - -export const ShowDockerContainersModal = ({ serverId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - - return ( - - - e.preventDefault()} - > - Show Docker Containers - - - -
- -
-
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-monitoring-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-monitoring-modal.tsx index d2b9d1c41..a318d0b40 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-monitoring-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-monitoring-modal.tsx @@ -1,6 +1,7 @@ +import { BarChartHorizontalBigIcon } from "lucide-react"; import { useState } from "react"; +import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { ShowPaidMonitoring } from "../../monitoring/paid/servers/show-paid-monitoring"; interface Props { @@ -14,12 +15,9 @@ export const ShowMonitoringModal = ({ url, token }: Props) => { return ( - e.preventDefault()} - > - Show Monitoring - +
diff --git a/apps/dokploy/components/dashboard/settings/servers/show-schedules-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-schedules-modal.tsx deleted file mode 100644 index dd3f67eae..000000000 --- a/apps/dokploy/components/dashboard/settings/servers/show-schedules-modal.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useState } from "react"; -import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; -import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; - -interface Props { - serverId: string; -} - -export const ShowSchedulesModal = ({ serverId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - - return ( - - - e.preventDefault()} - > - Show Schedules - - - - - - - ); -}; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index 832d04759..eaef46ad5 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -4,7 +4,6 @@ import { Key, KeyIcon, Loader2, - MoreHorizontal, Network, ServerIcon, Terminal, @@ -25,12 +24,6 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuLabel, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, @@ -38,16 +31,11 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal"; import { TerminalModal } from "../web-server/terminal-modal"; import { ShowServerActions } from "./actions/show-server-actions"; import { HandleServers } from "./handle-servers"; import { SetupServer } from "./setup-server"; -import { ShowDockerContainersModal } from "./show-docker-containers-modal"; import { ShowMonitoringModal } from "./show-monitoring-modal"; -import { ShowSchedulesModal } from "./show-schedules-modal"; -import { ShowSwarmOverviewModal } from "./show-swarm-overview-modal"; -import { ShowTraefikFileSystemModal } from "./show-traefik-file-system-modal"; import { WelcomeSubscription } from "./welcome-stripe/welcome-subscription"; export const ShowServers = () => { @@ -138,52 +126,6 @@ export const ShowServers = () => { {server.name}
- {isActive && - server.sshKeyId && - !isBuildServer && ( - - - - - - - Advanced - - - - {isCloud && ( - - )} - - - - - - )}
@@ -361,6 +303,27 @@ export const ShowServers = () => { )} + {isCloud && + server.sshKeyId && + !isBuildServer && ( + + +
+ +
+
+ +

Monitoring

+
+
+ )} +
{permissions?.server.delete && ( diff --git a/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx deleted file mode 100644 index 0add73e70..000000000 --- a/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useState } from "react"; -import { Card } from "@/components/ui/card"; -import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { ShowSwarmContainers } from "../../swarm/containers/show-swarm-containers"; -import SwarmMonitorCard from "../../swarm/monitoring-card"; - -interface Props { - serverId: string; -} - -export const ShowSwarmOverviewModal = ({ serverId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - - return ( - - - e.preventDefault()} - > - Show Swarm Overview - - - - - - Overview - Containers - - -
- -
-
- - -
- -
-
-
-
-
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-traefik-file-system-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-traefik-file-system-modal.tsx deleted file mode 100644 index c7f135acd..000000000 --- a/apps/dokploy/components/dashboard/settings/servers/show-traefik-file-system-modal.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useState } from "react"; -import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { ShowTraefikSystem } from "../../file-system/show-traefik-system"; - -interface Props { - serverId: string; -} - -export const ShowTraefikFileSystemModal = ({ serverId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - - return ( - - - e.preventDefault()} - > - Show Traefik File System - - - - - - - ); -}; diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index c9a4d5dfb..970c5a8a2 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -182,36 +182,31 @@ const MENU: Menu = { title: "Schedules", url: "/dashboard/schedules", icon: Clock, - // Only enabled in non-cloud environments - isEnabled: ({ isCloud, permissions }) => - !isCloud && !!permissions?.organization.update, + isEnabled: ({ permissions }) => !!permissions?.organization.update, }, { isSingle: true, title: "Traefik File System", url: "/dashboard/traefik", icon: GalleryVerticalEnd, - // Only enabled for users with access to Traefik files in non-cloud environments - isEnabled: ({ permissions, isCloud }) => - !!(permissions?.traefikFiles.read && !isCloud), + // Only enabled for users with access to Traefik files + isEnabled: ({ permissions }) => !!permissions?.traefikFiles.read, }, { isSingle: true, title: "Docker", url: "/dashboard/docker", icon: BlocksIcon, - // Only enabled for users with access to Docker in non-cloud environments - isEnabled: ({ permissions, isCloud }) => - !!(permissions?.docker.read && !isCloud), + // Only enabled for users with access to Docker + isEnabled: ({ permissions }) => !!permissions?.docker.read, }, { isSingle: true, title: "Swarm", url: "/dashboard/swarm", icon: PieChart, - // Only enabled for users with access to Docker in non-cloud environments - isEnabled: ({ permissions, isCloud }) => - !!(permissions?.docker.read && !isCloud), + // Only enabled for users with access to Docker + isEnabled: ({ permissions }) => !!permissions?.docker.read, }, { isSingle: true, @@ -375,9 +370,8 @@ const MENU: Menu = { title: "Cluster", url: "/dashboard/settings/cluster", icon: Boxes, - // Only enabled for admins in non-cloud environments - isEnabled: ({ permissions, isCloud }) => - !!(permissions?.organization.update && !isCloud), + // Only enabled for admins + isEnabled: ({ permissions }) => !!permissions?.organization.update, }, { isSingle: true, diff --git a/apps/dokploy/components/shared/server-filter.tsx b/apps/dokploy/components/shared/server-filter.tsx new file mode 100644 index 000000000..576528a05 --- /dev/null +++ b/apps/dokploy/components/shared/server-filter.tsx @@ -0,0 +1,156 @@ +import { Loader2, PlusIcon, ServerIcon } from "lucide-react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { Fragment, type ReactNode } from "react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { api } from "@/utils/api"; + +const DOKPLOY_SERVER = "dokploy-server"; + +interface Props { + children: (serverId?: string) => ReactNode; +} + +export const ServerFilter = ({ children }: Props) => { + const router = useRouter(); + const { data: servers, isLoading: isLoadingServers } = + api.server.withSSHKey.useQuery(); + const { data: isCloud, isLoading: isLoadingCloud } = + api.settings.isCloud.useQuery(); + const { data: permissions } = api.user.getPermissions.useQuery(); + + const queryServerId = + typeof router.query.serverId === "string" + ? router.query.serverId + : undefined; + + const selectedServer = servers?.find( + (server) => server.serverId === queryServerId, + ); + // Cloud has no local Dokploy server, so fall back to the first remote server + const serverId = selectedServer + ? selectedServer.serverId + : isCloud + ? servers?.[0]?.serverId + : undefined; + + const setServerId = (value: string) => { + const { serverId: _current, ...query } = router.query; + router.replace( + { + pathname: router.pathname, + query: value === DOKPLOY_SERVER ? query : { ...query, serverId: value }, + }, + undefined, + { shallow: true }, + ); + }; + + if (isLoadingServers || isLoadingCloud) { + return ( + +
+ + Loading... + + +
+
+ ); + } + + if (isCloud && !servers?.length) { + return ( + +
+
+ +
+
+ No servers yet + + {permissions?.server.create + ? "This section works on your remote servers. Add your first server to start managing it from here." + : "This section works on your remote servers. Ask an administrator to add a server to your organization."} + +
+ {permissions?.server.create && ( + + )} +
+
+ ); + } + + return ( +
+ {!!servers?.length && ( +
+ + +
+ )} + {children(serverId)} +
+ ); +}; diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index addf8c52b..df1b2e5ed 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -1,4 +1,3 @@ -import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; @@ -6,10 +5,15 @@ import type { ReactElement } from "react"; import superjson from "superjson"; import { ShowContainers } from "@/components/dashboard/docker/show/show-containers"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ServerFilter } from "@/components/shared/server-filter"; import { appRouter } from "@/server/api/root"; const Dashboard = () => { - return ; + return ( + + {(serverId) => } + + ); }; export default Dashboard; @@ -20,14 +24,6 @@ Dashboard.getLayout = (page: ReactElement) => { export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { - if (IS_CLOUD) { - return { - redirect: { - permanent: true, - destination: "/dashboard/home", - }, - }; - } const { user, session } = await validateRequest(ctx.req); if (!user) { return { diff --git a/apps/dokploy/pages/dashboard/schedules.tsx b/apps/dokploy/pages/dashboard/schedules.tsx index 459865010..1b576280d 100644 --- a/apps/dokploy/pages/dashboard/schedules.tsx +++ b/apps/dokploy/pages/dashboard/schedules.tsx @@ -1,20 +1,27 @@ -import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ServerFilter } from "@/components/shared/server-filter"; import { Card } from "@/components/ui/card"; function SchedulesPage() { return ( -
- -
- + + {(serverId) => ( +
+ +
+ +
+
- -
+ )} + ); } export default SchedulesPage; @@ -26,14 +33,6 @@ SchedulesPage.getLayout = (page: ReactElement) => { export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { - if (IS_CLOUD) { - return { - redirect: { - permanent: false, - destination: "/dashboard/home", - }, - }; - } const { user } = await validateRequest(ctx.req); if (!user || (user.role !== "owner" && user.role !== "admin")) { return { diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index a69db5b0c..8505272b9 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -1,17 +1,22 @@ -import { IS_CLOUD, validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import superjson from "superjson"; import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-nodes"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ServerFilter } from "@/components/shared/server-filter"; import { appRouter } from "@/server/api/root"; const Page = () => { return ( -
- -
+ + {(serverId) => ( +
+ +
+ )} +
); }; @@ -24,14 +29,6 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - if (IS_CLOUD) { - return { - redirect: { - permanent: false, - destination: "/dashboard/home", - }, - }; - } const { user, session } = await validateRequest(ctx.req); if (!user || user.role === "member") { return { diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index 40bdc0e71..77fc2b309 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -1,4 +1,3 @@ -import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; @@ -7,30 +6,35 @@ import superjson from "superjson"; import { ShowSwarmContainers } from "@/components/dashboard/swarm/containers/show-swarm-containers"; import SwarmMonitorCard from "@/components/dashboard/swarm/monitoring-card"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ServerFilter } from "@/components/shared/server-filter"; import { Card } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { appRouter } from "@/server/api/root"; const Dashboard = () => { return ( -
- - - Overview - Containers - - - - - - -
- -
-
-
-
-
+ + {(serverId) => ( +
+ + + Overview + Containers + + + + + + +
+ +
+
+
+
+
+ )} +
); }; @@ -42,14 +46,6 @@ Dashboard.getLayout = (page: ReactElement) => { export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { - if (IS_CLOUD) { - return { - redirect: { - permanent: false, - destination: "/dashboard/home", - }, - }; - } const { user, session } = await validateRequest(ctx.req); if (!user) { return { diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 0b6a7a477..91e9d010d 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -1,4 +1,3 @@ -import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; @@ -6,10 +5,15 @@ import type { ReactElement } from "react"; import superjson from "superjson"; import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ServerFilter } from "@/components/shared/server-filter"; import { appRouter } from "@/server/api/root"; const Dashboard = () => { - return ; + return ( + + {(serverId) => } + + ); }; export default Dashboard; @@ -20,14 +24,6 @@ Dashboard.getLayout = (page: ReactElement) => { export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { - if (IS_CLOUD) { - return { - redirect: { - permanent: false, - destination: "/dashboard/home", - }, - }; - } const { user, session } = await validateRequest(ctx.req); if (!user) { return {