Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80b8aae9fc |
No files matched your search
@@ -149,16 +149,20 @@ const createColumns = (t: TranslationFunction): MRT_ColumnDef<DockerContainer>[]
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function DockerTable({ containers, timestamp }: RouterOutputs["docker"]["getContainers"]) {
|
interface DockerTableProps {
|
||||||
|
initialData?: RouterOutputs["docker"]["getContainers"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DockerTable({ initialData }: DockerTableProps) {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const tDocker = useScopedI18n("docker");
|
const tDocker = useScopedI18n("docker");
|
||||||
const { data } = clientApi.docker.getContainers.useQuery(undefined, {
|
const { data, isFetching } = clientApi.docker.getContainers.useQuery(undefined, {
|
||||||
initialData: { containers, timestamp },
|
initialData,
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
});
|
});
|
||||||
const relativeTime = useTimeAgo(data.timestamp);
|
const relativeTime = useTimeAgo(data?.timestamp ?? new Date());
|
||||||
const utils = clientApi.useUtils();
|
const utils = clientApi.useUtils();
|
||||||
const { mutate, isPending } = clientApi.docker.invalidate.useMutation({
|
const { mutate, isPending } = clientApi.docker.invalidate.useMutation({
|
||||||
async onSuccess() {
|
async onSuccess() {
|
||||||
@@ -176,8 +180,11 @@ export function DockerTable({ containers, timestamp }: RouterOutputs["docker"]["
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const containers = data?.containers ?? [];
|
||||||
|
|
||||||
const table = useTranslatedMantineReactTable({
|
const table = useTranslatedMantineReactTable({
|
||||||
data: data.containers,
|
data: containers,
|
||||||
|
state: { isLoading: isFetching },
|
||||||
enableDensityToggle: false,
|
enableDensityToggle: false,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableColumnFilters: false,
|
enableColumnFilters: false,
|
||||||
@@ -190,7 +197,7 @@ export function DockerTable({ containers, timestamp }: RouterOutputs["docker"]["
|
|||||||
enableBottomToolbar: false,
|
enableBottomToolbar: false,
|
||||||
positionGlobalFilter: "right",
|
positionGlobalFilter: "right",
|
||||||
mantineSearchTextInputProps: {
|
mantineSearchTextInputProps: {
|
||||||
placeholder: tDocker("table.search", { count: String(data.containers.length) }),
|
placeholder: tDocker("table.search", { count: String(containers.length) }),
|
||||||
style: { minWidth: 300 },
|
style: { minWidth: 300 },
|
||||||
autoFocus: true,
|
autoFocus: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default async function DockerPage() {
|
|||||||
<DynamicBreadcrumb />
|
<DynamicBreadcrumb />
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={1}>{tDocker("title")}</Title>
|
<Title order={1}>{tDocker("title")}</Title>
|
||||||
<DockerTable containers={containers} timestamp={timestamp} />
|
<DockerTable initialData={{ containers, timestamp }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { createModal } from "@homarr/modals";
|
||||||
|
|
||||||
|
import { DockerTable } from "~/app/[locale]/manage/tools/docker/docker-table";
|
||||||
|
|
||||||
|
export const DockerQuickAccessModal = createModal<void>(() => <DockerTable />).withOptions({
|
||||||
|
defaultTitle: (t) => t("docker.title"),
|
||||||
|
size: 1400,
|
||||||
|
});
|
||||||
@@ -3,6 +3,7 @@ import { UnstyledButton } from "@mantine/core";
|
|||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
|
import { env } from "@homarr/docker/env";
|
||||||
|
|
||||||
import { CurrentUserAvatar } from "~/components/user-avatar";
|
import { CurrentUserAvatar } from "~/components/user-avatar";
|
||||||
import { UserAvatarMenu } from "~/components/user-avatar-menu";
|
import { UserAvatarMenu } from "~/components/user-avatar-menu";
|
||||||
@@ -11,9 +12,10 @@ import { UpdateIndicator } from "./update";
|
|||||||
export const UserButton = async () => {
|
export const UserButton = async () => {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
const isAdmin = session?.user.permissions.includes("admin");
|
const isAdmin = session?.user.permissions.includes("admin");
|
||||||
|
const isDockerEnabled = isAdmin && env.ENABLE_DOCKER;
|
||||||
const availableUpdatesPromise = isAdmin ? api.updateChecker.getAvailableUpdates() : undefined;
|
const availableUpdatesPromise = isAdmin ? api.updateChecker.getAvailableUpdates() : undefined;
|
||||||
return (
|
return (
|
||||||
<UserAvatarMenu availableUpdatesPromise={availableUpdatesPromise}>
|
<UserAvatarMenu availableUpdatesPromise={availableUpdatesPromise} isDockerEnabled={isDockerEnabled}>
|
||||||
<UnstyledButton>
|
<UnstyledButton>
|
||||||
<Suspense fallback={<CurrentUserAvatar size="md" />}>
|
<Suspense fallback={<CurrentUserAvatar size="md" />}>
|
||||||
<UpdateIndicator availableUpdatesPromise={availableUpdatesPromise} disabled={!isAdmin}>
|
<UpdateIndicator availableUpdatesPromise={availableUpdatesPromise} disabled={!isAdmin}>
|
||||||
|
|||||||
@@ -5,7 +5,15 @@ import { useCallback, useEffect } from "react";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core";
|
import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core";
|
||||||
import { useHotkeys, useTimeout } from "@mantine/hooks";
|
import { useHotkeys, useTimeout } from "@mantine/hooks";
|
||||||
import { IconCheck, IconHome, IconLogin, IconLogout, IconSettings, IconTool } from "@tabler/icons-react";
|
import {
|
||||||
|
IconBrandDocker,
|
||||||
|
IconCheck,
|
||||||
|
IconHome,
|
||||||
|
IconLogin,
|
||||||
|
IconLogout,
|
||||||
|
IconSettings,
|
||||||
|
IconTool,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
|
||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { signOut, useSession } from "@homarr/auth/client";
|
import { signOut, useSession } from "@homarr/auth/client";
|
||||||
@@ -17,14 +25,16 @@ import { Link } from "@homarr/ui";
|
|||||||
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
|
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
|
||||||
import { CurrentColorSchemeCombobox } from "./color-scheme/current-color-scheme-combobox";
|
import { CurrentColorSchemeCombobox } from "./color-scheme/current-color-scheme-combobox";
|
||||||
import { CurrentLanguageCombobox } from "./language/current-language-combobox";
|
import { CurrentLanguageCombobox } from "./language/current-language-combobox";
|
||||||
|
import { DockerQuickAccessModal } from "./layout/header/docker-quick-access-modal";
|
||||||
import { AvailableUpdatesMenuItem } from "./layout/header/update";
|
import { AvailableUpdatesMenuItem } from "./layout/header/update";
|
||||||
|
|
||||||
interface UserAvatarMenuProps {
|
interface UserAvatarMenuProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
availableUpdatesPromise?: Promise<RouterOutputs["updateChecker"]["getAvailableUpdates"]>;
|
availableUpdatesPromise?: Promise<RouterOutputs["updateChecker"]["getAvailableUpdates"]>;
|
||||||
|
isDockerEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserAvatarMenu = ({ children, availableUpdatesPromise }: UserAvatarMenuProps) => {
|
export const UserAvatarMenu = ({ children, availableUpdatesPromise, isDockerEnabled }: UserAvatarMenuProps) => {
|
||||||
const t = useScopedI18n("common.userAvatar.menu");
|
const t = useScopedI18n("common.userAvatar.menu");
|
||||||
const { toggleColorScheme } = useMantineColorScheme();
|
const { toggleColorScheme } = useMantineColorScheme();
|
||||||
useHotkeys([[hotkeys.toggleColorScheme, toggleColorScheme]]);
|
useHotkeys([[hotkeys.toggleColorScheme, toggleColorScheme]]);
|
||||||
@@ -34,6 +44,7 @@ export const UserAvatarMenu = ({ children, availableUpdatesPromise }: UserAvatar
|
|||||||
|
|
||||||
const { logoutUrl } = useAuthContext();
|
const { logoutUrl } = useAuthContext();
|
||||||
const { openModal } = useModalAction(LogoutModal);
|
const { openModal } = useModalAction(LogoutModal);
|
||||||
|
const { openModal: openDockerModal } = useModalAction(DockerQuickAccessModal);
|
||||||
|
|
||||||
const handleSignout = useCallback(async () => {
|
const handleSignout = useCallback(async () => {
|
||||||
await signOut({
|
await signOut({
|
||||||
@@ -80,6 +91,11 @@ export const UserAvatarMenu = ({ children, availableUpdatesPromise }: UserAvatar
|
|||||||
<Menu.Item component={Link} href="/manage" leftSection={<IconTool size="1rem" />}>
|
<Menu.Item component={Link} href="/manage" leftSection={<IconTool size="1rem" />}>
|
||||||
{t("management")}
|
{t("management")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
{isDockerEnabled && (
|
||||||
|
<Menu.Item leftSection={<IconBrandDocker size="1rem" />} onClick={() => openDockerModal()}>
|
||||||
|
{t("docker")}
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
|
|||||||
@@ -1467,6 +1467,7 @@
|
|||||||
"switchToDarkMode": "Switch to dark mode",
|
"switchToDarkMode": "Switch to dark mode",
|
||||||
"switchToLightMode": "Switch to light mode",
|
"switchToLightMode": "Switch to light mode",
|
||||||
"management": "Management",
|
"management": "Management",
|
||||||
|
"docker": "Docker",
|
||||||
"preferences": "Your preferences",
|
"preferences": "Your preferences",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
@@ -4295,7 +4296,7 @@
|
|||||||
"notStarted": "Not started"
|
"notStarted": "Not started"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"management": "Management",
|
"management": "Management",
|
||||||
"dashboard": "Dashboard"
|
"dashboard": "Dashboard"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user