Merge pull request #1106 from DJKnaeckebrot/fix/styling-after-sidebar-fixes

fix(style): clean up sidebar related style issue
This commit is contained in:
Mauricio Siu
2025-01-13 23:41:00 -06:00
committed by GitHub
8 changed files with 322 additions and 315 deletions

View File

@@ -1,13 +1,13 @@
import { import {
type ColumnFiltersState, type ColumnFiltersState,
type SortingState, type SortingState,
type VisibilityState, type VisibilityState,
flexRender, flexRender,
getCoreRowModel, getCoreRowModel,
getFilteredRowModel, getFilteredRowModel,
getPaginationRowModel, getPaginationRowModel,
getSortedRowModel, getSortedRowModel,
useReactTable, useReactTable,
} from "@tanstack/react-table"; } from "@tanstack/react-table";
import { ChevronDown, Container } from "lucide-react"; import { ChevronDown, Container } from "lucide-react";
import * as React from "react"; import * as React from "react";
@@ -15,228 +15,228 @@ import * as React from "react";
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { import {
Table, Table,
TableBody, TableBody,
TableCell, TableCell,
TableHead, TableHead,
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { type RouterOutputs, api } from "@/utils/api"; import { type RouterOutputs, api } from "@/utils/api";
import { columns } from "./colums"; import { columns } from "./colums";
export type Container = NonNullable< export type Container = NonNullable<
RouterOutputs["docker"]["getContainers"] RouterOutputs["docker"]["getContainers"]
>[0]; >[0];
interface Props { interface Props {
serverId?: string; serverId?: string;
} }
export const ShowContainers = ({ serverId }: Props) => { export const ShowContainers = ({ serverId }: Props) => {
const { data, isLoading } = api.docker.getContainers.useQuery({ const { data, isLoading } = api.docker.getContainers.useQuery({
serverId, serverId,
}); });
const [sorting, setSorting] = React.useState<SortingState>([]); const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>( const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[], []
); );
const [columnVisibility, setColumnVisibility] = const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({}); React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({});
const table = useReactTable({ const table = useReactTable({
data: data ?? [], data: data ?? [],
columns, columns,
onSortingChange: setSorting, onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters, onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(), getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(), getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility, onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection, onRowSelectionChange: setRowSelection,
state: { state: {
sorting, sorting,
columnFilters, columnFilters,
columnVisibility, columnVisibility,
rowSelection, rowSelection,
}, },
}); });
return ( return (
<div className="w-full"> <div className="w-full">
<Card className="h-full bg-sidebar p-2.5 rounded-xl max-w-8xl mx-auto"> <Card className="h-full bg-sidebar p-2.5 rounded-xl">
<div className="rounded-xl bg-background shadow-md "> <div className="rounded-xl bg-background shadow-md ">
<CardHeader className=""> <CardHeader className="">
<CardTitle className="text-xl flex flex-row gap-2"> <CardTitle className="text-xl flex flex-row gap-2">
<Container className="size-6 text-muted-foreground self-center" /> <Container className="size-6 text-muted-foreground self-center" />
Docker Containers Docker Containers
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
See all the containers of your dokploy server See all the containers of your dokploy server
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-2 py-8 border-t"> <CardContent className="space-y-2 py-8 border-t">
<div className="gap-4 pb-20 w-full"> <div className="gap-4 pb-20 w-full">
<div className="flex flex-col gap-4 w-full overflow-auto"> <div className="flex flex-col gap-4 w-full overflow-auto">
<div className="flex items-center gap-2 max-sm:flex-wrap"> <div className="flex items-center gap-2 max-sm:flex-wrap">
<Input <Input
placeholder="Filter by name..." placeholder="Filter by name..."
value={ value={
(table.getColumn("name")?.getFilterValue() as string) ?? (table.getColumn("name")?.getFilterValue() as string) ??
"" ""
} }
onChange={(event) => onChange={(event) =>
table table
.getColumn("name") .getColumn("name")
?.setFilterValue(event.target.value) ?.setFilterValue(event.target.value)
} }
className="md:max-w-sm" className="md:max-w-sm"
/> />
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
variant="outline" variant="outline"
className="sm:ml-auto max-sm:w-full" className="sm:ml-auto max-sm:w-full"
> >
Columns <ChevronDown className="ml-2 h-4 w-4" /> Columns <ChevronDown className="ml-2 h-4 w-4" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
{table {table
.getAllColumns() .getAllColumns()
.filter((column) => column.getCanHide()) .filter((column) => column.getCanHide())
.map((column) => { .map((column) => {
return ( return (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
key={column.id} key={column.id}
className="capitalize" className="capitalize"
checked={column.getIsVisible()} checked={column.getIsVisible()}
onCheckedChange={(value) => onCheckedChange={(value) =>
column.toggleVisibility(!!value) column.toggleVisibility(!!value)
} }
> >
{column.id} {column.id}
</DropdownMenuCheckboxItem> </DropdownMenuCheckboxItem>
); );
})} })}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
<div className="rounded-md border"> <div className="rounded-md border">
{isLoading ? ( {isLoading ? (
<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]">
<span className="text-muted-foreground text-lg font-medium"> <span className="text-muted-foreground text-lg font-medium">
Loading... Loading...
</span> </span>
</div> </div>
) : data?.length === 0 ? ( ) : data?.length === 0 ? (
<div className="flex-col gap-2 flex items-center justify-center h-[55vh]"> <div className="flex-col gap-2 flex items-center justify-center h-[55vh]">
<span className="text-muted-foreground text-lg font-medium"> <span className="text-muted-foreground text-lg font-medium">
No results. No results.
</span> </span>
</div> </div>
) : ( ) : (
<Table> <Table>
<TableHeader> <TableHeader>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}> <TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => { {headerGroup.headers.map((header) => {
return ( return (
<TableHead key={header.id}> <TableHead key={header.id}>
{header.isPlaceholder {header.isPlaceholder
? null ? null
: flexRender( : flexRender(
header.column.columnDef.header, header.column.columnDef.header,
header.getContext(), header.getContext()
)} )}
</TableHead> </TableHead>
); );
})} })}
</TableRow> </TableRow>
))} ))}
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{table?.getRowModel()?.rows?.length ? ( {table?.getRowModel()?.rows?.length ? (
table.getRowModel().rows.map((row) => ( table.getRowModel().rows.map((row) => (
<TableRow <TableRow
key={row.id} key={row.id}
data-state={row.getIsSelected() && "selected"} data-state={row.getIsSelected() && "selected"}
> >
{row.getVisibleCells().map((cell) => ( {row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}> <TableCell key={cell.id}>
{flexRender( {flexRender(
cell.column.columnDef.cell, cell.column.columnDef.cell,
cell.getContext(), cell.getContext()
)} )}
</TableCell> </TableCell>
))} ))}
</TableRow> </TableRow>
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell <TableCell
colSpan={columns.length} colSpan={columns.length}
className="h-24 text-center" className="h-24 text-center"
> >
{isLoading ? ( {isLoading ? (
<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]">
<span className="text-muted-foreground text-lg font-medium"> <span className="text-muted-foreground text-lg font-medium">
Loading... Loading...
</span> </span>
</div> </div>
) : ( ) : (
<>No results.</> <>No results.</>
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
</TableBody> </TableBody>
</Table> </Table>
)} )}
</div> </div>
{data && data?.length > 0 && ( {data && data?.length > 0 && (
<div className="flex items-center justify-end space-x-2 py-4"> <div className="flex items-center justify-end space-x-2 py-4">
<div className="space-x-2 flex flex-wrap"> <div className="space-x-2 flex flex-wrap">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => table.previousPage()} onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()} disabled={!table.getCanPreviousPage()}
> >
Previous Previous
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => table.nextPage()} onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()} disabled={!table.getCanNextPage()}
> >
Next Next
</Button> </Button>
</div> </div>
</div> </div>
)} )}
</div> </div>
</div> </div>
</CardContent> </CardContent>
</div> </div>
</Card> </Card>
</div> </div>
); );
}; };

View File

@@ -1,10 +1,10 @@
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Tree } from "@/components/ui/file-tree"; import { Tree } from "@/components/ui/file-tree";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -14,97 +14,97 @@ import React from "react";
import { ShowTraefikFile } from "./show-traefik-file"; import { ShowTraefikFile } from "./show-traefik-file";
interface Props { interface Props {
serverId?: string; serverId?: string;
} }
export const ShowTraefikSystem = ({ serverId }: Props) => { export const ShowTraefikSystem = ({ serverId }: Props) => {
const [file, setFile] = React.useState<null | string>(null); const [file, setFile] = React.useState<null | string>(null);
const { const {
data: directories, data: directories,
isLoading, isLoading,
error, error,
isError, isError,
} = api.settings.readDirectories.useQuery( } = api.settings.readDirectories.useQuery(
{ {
serverId, serverId,
}, },
{ {
retry: 2, retry: 2,
}, }
); );
return ( return (
<div className="w-full"> <div className="w-full">
<Card className="h-full bg-sidebar p-2.5 rounded-xl max-w-8xl mx-auto"> <Card className="h-full bg-sidebar p-2.5 rounded-xl">
<div className="rounded-xl bg-background shadow-md "> <div className="rounded-xl bg-background shadow-md ">
<CardHeader className=""> <CardHeader className="">
<CardTitle className="text-xl flex flex-row gap-2"> <CardTitle className="text-xl flex flex-row gap-2">
<FileIcon className="size-6 text-muted-foreground self-center" /> <FileIcon className="size-6 text-muted-foreground self-center" />
Traefik File System Traefik File System
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
Manage all the files and directories in {"'/etc/dokploy/traefik'"} Manage all the files and directories in {"'/etc/dokploy/traefik'"}
. .
</CardDescription> </CardDescription>
<AlertBlock type="warning"> <AlertBlock type="warning">
Adding invalid configuration to existing files, can break your Adding invalid configuration to existing files, can break your
Traefik instance, preventing access to your applications. Traefik instance, preventing access to your applications.
</AlertBlock> </AlertBlock>
</CardHeader> </CardHeader>
<CardContent className="space-y-2 py-8 border-t"> <CardContent className="space-y-2 py-8 border-t">
<div> <div>
<div className="flex flex-col lg:flex-row gap-4 md:gap-10 w-full"> <div className="flex flex-col lg:flex-row gap-4 md:gap-10 w-full">
{isError && ( {isError && (
<AlertBlock type="error" className="w-full"> <AlertBlock type="error" className="w-full">
{error?.message} {error?.message}
</AlertBlock> </AlertBlock>
)} )}
{isLoading && ( {isLoading && (
<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]">
<span className="text-muted-foreground text-lg font-medium"> <span className="text-muted-foreground text-lg font-medium">
Loading... Loading...
</span> </span>
<Loader2 className="animate-spin size-8 text-muted-foreground" /> <Loader2 className="animate-spin size-8 text-muted-foreground" />
</div> </div>
)} )}
{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]">
<span className="text-muted-foreground text-lg font-medium"> <span className="text-muted-foreground text-lg font-medium">
No directories or files detected in{" "} No directories or files detected in{" "}
{"'/etc/dokploy/traefik'"} {"'/etc/dokploy/traefik'"}
</span> </span>
<Folder className="size-8 text-muted-foreground" /> <Folder className="size-8 text-muted-foreground" />
</div> </div>
)} )}
{directories && directories?.length > 0 && ( {directories && directories?.length > 0 && (
<> <>
<Tree <Tree
data={directories} data={directories}
className="lg:max-w-[19rem] w-full lg:h-[660px] border rounded-lg" className="lg:max-w-[19rem] w-full lg:h-[660px] border rounded-lg"
onSelectChange={(item) => setFile(item?.id || null)} onSelectChange={(item) => setFile(item?.id || null)}
folderIcon={Folder} folderIcon={Folder}
itemIcon={Workflow} itemIcon={Workflow}
/> />
<div className="w-full"> <div className="w-full">
{file ? ( {file ? (
<ShowTraefikFile path={file} serverId={serverId} /> <ShowTraefikFile path={file} serverId={serverId} />
) : ( ) : (
<div className="h-full w-full flex-col gap-2 flex items-center justify-center"> <div className="h-full w-full flex-col gap-2 flex items-center justify-center">
<span className="text-muted-foreground text-lg font-medium"> <span className="text-muted-foreground text-lg font-medium">
No file selected No file selected
</span> </span>
<FileIcon className="size-8 text-muted-foreground" /> <FileIcon className="size-8 text-muted-foreground" />
</div> </div>
)} )}
</div> </div>
</> </>
)} )}
</div> </div>
</div> </div>
</CardContent> </CardContent>
</div> </div>
</Card> </Card>
</div> </div>
); );
}; };

View File

@@ -3,7 +3,7 @@ import { DockerMonitoring } from "../docker/show";
export const ShowMonitoring = () => { export const ShowMonitoring = () => {
return ( return (
<div className="my-6 w-full "> <div className="w-full">
<DockerMonitoring appName="dokploy" /> <DockerMonitoring appName="dokploy" />
</div> </div>
); );

View File

@@ -75,7 +75,7 @@ export const ShowProjects = () => {
list={[{ name: "Projects", href: "/dashboard/projects" }]} list={[{ name: "Projects", href: "/dashboard/projects" }]}
/> />
<div className="w-full"> <div className="w-full">
<Card className="h-full bg-sidebar p-2.5 rounded-xl "> <Card className="h-full bg-sidebar p-2.5 rounded-xl ">
<div className="rounded-xl bg-background shadow-md "> <div className="rounded-xl bg-background shadow-md ">
<div className="flex justify-between gap-4 w-full items-center"> <div className="flex justify-between gap-4 w-full items-center">
<CardHeader className=""> <CardHeader className="">

View File

@@ -70,7 +70,7 @@ export default function SwarmMonitorCard({ serverId }: Props) {
); );
return ( return (
<Card className="h-full bg-sidebar p-2.5 rounded-xl max-w-8xl mx-auto w-full"> <Card className="h-full bg-sidebar p-2.5 rounded-xl mx-auto w-full">
<div className="rounded-xl bg-background shadow-md p-6 flex flex-col gap-4"> <div className="rounded-xl bg-background shadow-md p-6 flex flex-col gap-4">
<header className="flex items-center justify-between"> <header className="flex items-center justify-between">
<div className="space-y-1"> <div className="space-y-1">
@@ -94,7 +94,7 @@ export default function SwarmMonitorCard({ serverId }: Props) {
)} )}
</header> </header>
<div className="grid gap-6 md:grid-cols-3"> <div className="grid gap-6 lg:grid-cols-3">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Nodes</CardTitle> <CardTitle className="text-sm font-medium">Total Nodes</CardTitle>

View File

@@ -8,6 +8,7 @@ import {
BlocksIcon, BlocksIcon,
BookIcon, BookIcon,
ChevronRight, ChevronRight,
CircleHelp,
Command, Command,
CreditCard, CreditCard,
Database, Database,
@@ -127,7 +128,7 @@ const data = {
isActive: false, isActive: false,
}, },
{ {
title: "File System", title: "Traefik File System",
url: "/dashboard/traefik", url: "/dashboard/traefik",
icon: GalleryVerticalEnd, icon: GalleryVerticalEnd,
isSingle: true, isSingle: true,
@@ -317,9 +318,15 @@ const data = {
}, },
{ {
name: "Support", name: "Support",
url: "https://discord.gg/2tBnJ3jDJc",
icon: CircleHelp,
},
{
name: "Sponsor",
url: "https://opencollective.com/dokploy", url: "https://opencollective.com/dokploy",
icon: Heart, icon: Heart,
}, },
// { // {
// name: "Sales & Marketing", // name: "Sales & Marketing",
// url: "#", // url: "#",

View File

@@ -26,7 +26,7 @@ const Page = () => {
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
return ( return (
<div className="w-full"> <div className="w-full">
<div className="h-full p-2.5 rounded-xl max-w-5xl mx-auto flex flex-col gap-4"> <div className="h-full rounded-xl max-w-5xl mx-auto flex flex-col gap-4">
<ProfileForm /> <ProfileForm />
{(user?.canAccessToAPI || data?.rol === "admin") && <GenerateToken />} {(user?.canAccessToAPI || data?.rol === "admin") && <GenerateToken />}

View File

@@ -13,7 +13,7 @@ import superjson from "superjson";
const Page = () => { const Page = () => {
return ( return (
<div className="w-full"> <div className="w-full">
<div className="h-full p-2.5 rounded-xl max-w-5xl mx-auto flex flex-col gap-4"> <div className="h-full rounded-xl max-w-5xl mx-auto flex flex-col gap-4">
<WebDomain /> <WebDomain />
<WebServer /> <WebServer />
</div> </div>