Skip to content

Commit

Permalink
feat(settings): add simple-ping settings (#2118)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meierschlumpf authored Feb 7, 2025
1 parent c04c42d commit dff6cb9
Show file tree
Hide file tree
Showing 88 changed files with 4,489 additions and 582 deletions.
1 change: 1 addition & 0 deletions apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@homarr/analytics": "workspace:^0.1.0",
"@homarr/api": "workspace:^0.1.0",
"@homarr/auth": "workspace:^0.1.0",
"@homarr/boards": "workspace:^0.1.0",
"@homarr/certificates": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
"@homarr/cron-job-status": "workspace:^0.1.0",
Expand Down
3 changes: 2 additions & 1 deletion apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { Box, LoadingOverlay, Stack } from "@mantine/core";

import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";

import { BoardCategorySection } from "~/components/board/sections/category-section";
import { BoardEmptySection } from "~/components/board/sections/empty-section";
import { BoardBackgroundVideo } from "~/components/layout/background";
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
import { useIsBoardReady, useRequiredBoard } from "./_context";
import { useIsBoardReady } from "./_ready-context";

let boardName: string | null = null;

Expand Down
118 changes: 0 additions & 118 deletions apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useRequiredBoard } from "./_context";
import { useRequiredBoard } from "@homarr/boards/context";

export const CustomCss = () => {
const board = useRequiredBoard();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
} from "@tabler/icons-react";

import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
Expand All @@ -32,7 +34,6 @@ import { CategoryEditModal } from "~/components/board/sections/category/category
import { useDynamicSectionActions } from "~/components/board/sections/dynamic/dynamic-actions";
import { HeaderButton } from "~/components/layout/header/button";
import { env } from "~/env";
import { useEditMode, useRequiredBoard } from "./_context";

export const BoardContentHeaderActions = () => {
const [isEditMode] = useEditMode();
Expand Down Expand Up @@ -119,7 +120,7 @@ const AddMenu = () => {
};

const EditModeMenu = () => {
const [isEditMode, setEditMode] = useEditMode();
const [isEditMode, { open, close }] = useEditMode();
const board = useRequiredBoard();
const utils = clientApi.useUtils();
const t = useScopedI18n("board.action.edit");
Expand All @@ -131,7 +132,7 @@ const EditModeMenu = () => {
});
void utils.board.getBoardByName.invalidate({ name: board.name });
void revalidatePathActionAsync(`/boards/${board.name}`);
setEditMode(false);
close();
},
onError() {
showErrorNotification({
Expand All @@ -143,8 +144,8 @@ const EditModeMenu = () => {

const toggle = useCallback(() => {
if (isEditMode) return saveBoard(board);
setEditMode(true);
}, [board, isEditMode, saveBoard, setEditMode]);
open();
}, [board, isEditMode, saveBoard, open]);

useHotkeys([["mod+e", toggle]]);
usePreventLeaveWithDirty(isEditMode);
Expand Down
67 changes: 67 additions & 0 deletions apps/nextjs/src/app/[locale]/boards/(content)/_ready-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import type { PropsWithChildren } from "react";
import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { usePathname } from "next/navigation";

import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";

const BoardReadyContext = createContext<{
isReady: boolean;
markAsReady: (id: string) => void;
} | null>(null);

export const BoardReadyProvider = ({ children }: PropsWithChildren) => {
const pathname = usePathname();
const utils = clientApi.useUtils();
const board = useRequiredBoard();
const [readySections, setReadySections] = useState<string[]>([]);

// Reset sections required for ready state
useEffect(() => {
return () => {
setReadySections([]);
};
}, [pathname, utils]);

useEffect(() => {
setReadySections((previous) => previous.filter((id) => board.sections.some((section) => section.id === id)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [board.sections.length, setReadySections]);

const markAsReady = useCallback((id: string) => {
setReadySections((previous) => (previous.includes(id) ? previous : [...previous, id]));
}, []);

return (
<BoardReadyContext.Provider
value={{
isReady: board.sections.length === readySections.length,
markAsReady,
}}
>
{children}
</BoardReadyContext.Provider>
);
};

export const useMarkSectionAsReady = () => {
const context = useContext(BoardReadyContext);

if (!context) {
throw new Error("BoardReadyProvider is required");
}

return context.markAsReady;
};

export const useIsBoardReady = () => {
const context = useContext(BoardReadyContext);

if (!context) {
throw new Error("BoardReadyProvider is required");
}

return context.isReady;
};
2 changes: 1 addition & 1 deletion apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { PropsWithChildren } from "react";
import type { MantineColorsTuple } from "@mantine/core";
import { createTheme, darken, lighten, MantineProvider } from "@mantine/core";

import { useRequiredBoard } from "@homarr/boards/context";
import type { ColorScheme } from "@homarr/definitions";

import { useColorSchemeManager } from "../../_client-providers/mantine";
import { useRequiredBoard } from "./_context";

export const BoardMantineProvider = ({
children,
Expand Down
48 changes: 48 additions & 0 deletions apps/nextjs/src/app/[locale]/boards/[name]/settings/_behavior.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use client";

import { Button, Group, Stack, Switch } from "@mantine/core";

import { useForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";

import type { Board } from "../../_types";
import { useSavePartialSettingsMutation } from "./_shared";

interface Props {
board: Board;
}

export const BehaviorSettingsContent = ({ board }: Props) => {
const t = useI18n();
const { mutate: savePartialSettings, isPending } = useSavePartialSettingsMutation(board);
const form = useForm({
initialValues: {
disableStatus: board.disableStatus,
},
});

return (
<form
onSubmit={form.onSubmit((values) => {
savePartialSettings({
id: board.id,
...values,
});
})}
>
<Stack>
<Switch
label={t("board.field.disableStatus.label")}
description={t("board.field.disableStatus.description")}
{...form.getInputProps("disableStatus", { type: "checkbox" })}
/>

<Group justify="end">
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
</Stack>
</form>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { useRouter } from "next/navigation";
import { Button, Divider, Group, Stack, Text } from "@mantine/core";

import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";

import { BoardRenameModal } from "~/components/board/modals/board-rename-modal";
import { useRequiredBoard } from "../../(content)/_context";
import classes from "./danger.module.css";

export const DangerZoneSettingsContent = ({ hideVisibility }: { hideVisibility: boolean }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { Button, Grid, Group, Loader, Stack, TextInput, Tooltip } from "@mantine
import { useDebouncedValue, useDocumentTitle, useFavicon } from "@mantine/hooks";
import { IconAlertTriangle } from "@tabler/icons-react";

import { useUpdateBoard } from "@homarr/boards/updater";
import { useZodForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";
import { validation } from "@homarr/validation";

import { createMetaTitle } from "~/metadata";
import type { Board } from "../../_types";
import { useUpdateBoard } from "../../(content)/_client";
import { useSavePartialSettingsMutation } from "./_shared";

interface Props {
Expand Down
5 changes: 5 additions & 0 deletions apps/nextjs/src/app/[locale]/boards/[name]/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AccordionControl, AccordionItem, AccordionPanel, Container, Stack, Text
import {
IconAlertTriangle,
IconBrush,
IconClick,
IconFileTypeCss,
IconLayout,
IconPhoto,
Expand All @@ -23,6 +24,7 @@ import type { TablerIcon } from "@homarr/ui";
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
import { ActiveTabAccordion } from "../../../../../components/active-tab-accordion";
import { BackgroundSettingsContent } from "./_background";
import { BehaviorSettingsContent } from "./_behavior";
import { BoardAccessSettings } from "./_board-access";
import { ColorSettingsContent } from "./_colors";
import { CustomCssSettingsContent } from "./_customCss";
Expand Down Expand Up @@ -95,6 +97,9 @@ export default async function BoardSettingsPage(props: Props) {
<AccordionItemFor value="customCss" icon={IconFileTypeCss}>
<CustomCssSettingsContent board={board} />
</AccordionItemFor>
<AccordionItemFor value="behavior" icon={IconClick}>
<BehaviorSettingsContent board={board} />
</AccordionItemFor>
{hasFullAccess && (
<>
<AccordionItemFor value="access" icon={IconUser}>
Expand Down
3 changes: 2 additions & 1 deletion apps/nextjs/src/app/[locale]/boards/_header-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import { IconLayoutBoard } from "@tabler/icons-react";

import { useRequiredBoard } from "@homarr/boards/context";

import { HeaderButton } from "~/components/layout/header/button";
import { useRequiredBoard } from "./(content)/_context";

export const BoardOtherHeaderActions = () => {
const board = useRequiredBoard();
Expand Down
Loading

0 comments on commit dff6cb9

Please sign in to comment.