"use client";
import { Badge, Card, Collapse, Group, Stack, Title, UnstyledButton } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
import type {
Board,
CategorySection,
DynamicSectionItem,
EmptySection,
SectionItem,
} from "~/app/[locale]/boards/_types";
import { BoardItemContent } from "../items/item-content";
import { CategoryMenu } from "./category/category-menu";
import classes from "./item.module.css";
interface StaticBoardGridProps {
board: Board;
}
export const StaticBoardGrid = ({ board }: StaticBoardGridProps) => {
const currentLayoutId = useCurrentLayout();
const currentLayout = board.layouts.find((layout) => layout.id === currentLayoutId);
const columnCount = currentLayout?.columnCount ?? 12;
const fullWidthSortedSections = board.sections
.filter(
(section): section is CategorySection | EmptySection => section.kind === "empty" || section.kind === "category",
)
.sort((sectionA, sectionB) => sectionA.yOffset - sectionB.yOffset);
return (
{fullWidthSortedSections.map((section) =>
section.kind === "empty" ? (
) : (
),
)}
);
};
interface StaticSectionGridProps {
sectionId: string;
board: Board;
layoutId: string;
columnCount: number;
isDynamic?: boolean;
fixedRowCount?: number;
}
const StaticSectionGrid = ({
sectionId,
board,
layoutId,
columnCount,
isDynamic,
fixedRowCount,
}: StaticSectionGridProps) => {
const items = getSectionItems(board, sectionId, layoutId);
const innerSections = getDynamicSections(board, sectionId, layoutId);
const allItems = [...items, ...innerSections].sort((itemA, itemB) => {
if (itemA.yOffset === itemB.yOffset) return itemA.xOffset - itemB.xOffset;
return itemA.yOffset - itemB.yOffset;
});
if (allItems.length === 0) return null;
const rowCount = fixedRowCount ?? Math.max(...allItems.map((item) => item.yOffset + item.height));
return (
{allItems.map((item) => (
))}
);
};
interface StaticGridItemProps {
item: SectionItem | DynamicSectionItem;
board: Board;
layoutId: string;
}
const StaticGridItem = ({ item, board, layoutId }: StaticGridItemProps) => (
{item.type === "item" ? (
) : (
)}
);
interface StaticEmptySectionProps {
section: EmptySection;
board: Board;
layoutId: string;
columnCount: number;
}
const StaticEmptySection = ({ section, board, layoutId, columnCount }: StaticEmptySectionProps) => {
const items = getSectionItems(board, section.id, layoutId);
const innerSections = getDynamicSections(board, section.id, layoutId);
if (items.length === 0 && innerSections.length === 0) return null;
return ;
};
interface StaticCategorySectionProps {
section: CategorySection;
board: Board;
layoutId: string;
columnCount: number;
}
const StaticCategorySection = ({ section, board, layoutId, columnCount }: StaticCategorySectionProps) => {
const { mutate } = clientApi.section.changeCollapsed.useMutation();
const boardData = useRequiredBoard();
const [opened, { toggle }] = useDisclosure(section.collapsed);
const handleToggle = () => {
toggle();
mutate({ sectionId: section.id, collapsed: !opened });
};
return (
{opened ? : }
{section.name}
);
};
interface StaticDynamicSectionProps {
section: DynamicSectionItem;
board: Board;
layoutId: string;
}
const StaticDynamicSection = ({ section, board, layoutId }: StaticDynamicSectionProps) => {
const boardData = useRequiredBoard();
const options = section.options;
return (
{options.title && (
{options.title}
)}
);
};
function getSectionItems(board: Board, sectionId: string, layoutId: string): SectionItem[] {
return board.items
.map(({ layouts, ...item }) => {
const layout = layouts.find((candidate) => candidate.layoutId === layoutId);
if (!layout) return null;
return { ...layout, ...item, type: "item" as const };
})
.filter((item): item is SectionItem => item !== null && item.sectionId === sectionId);
}
function getDynamicSections(board: Board, sectionId: string, layoutId: string): DynamicSectionItem[] {
return board.sections
.filter((section) => section.kind === "dynamic")
.map(({ layouts, ...section }) => {
const layout = layouts.find((candidate) => candidate.layoutId === layoutId);
if (!layout) return null;
return { ...layout, ...section, type: "section" as const };
})
.filter((entry): entry is DynamicSectionItem => entry !== null && entry.parentSectionId === sectionId);
}