mirror of
https://github.com/docmost/docmost.git
synced 2026-06-10 01:52:43 +08:00
feat(bases): add kanban empty state with group-by picker
This commit is contained in:
@@ -5,7 +5,9 @@ import {
|
||||
IBaseView,
|
||||
} from "@/features/base/types/base.types";
|
||||
import { useKanbanGroups } from "@/features/base/hooks/use-kanban-groups";
|
||||
import { useUpdateViewMutation } from "@/features/base/queries/base-view-query";
|
||||
import { KanbanColumn } from "./kanban-column";
|
||||
import { KanbanEmptyState } from "./kanban-empty-state";
|
||||
import classes from "@/features/base/styles/kanban.module.css";
|
||||
|
||||
type BaseKanbanProps = {
|
||||
@@ -34,6 +36,10 @@ export function BaseKanban({
|
||||
[base.properties],
|
||||
);
|
||||
const isGroupable = property?.type === "select" || property?.type === "status";
|
||||
const updateViewMutation = useUpdateViewMutation();
|
||||
|
||||
// Rules of Hooks: call useKanbanGroups unconditionally with `undefined`
|
||||
// when not groupable; switch the render path on isGroupable below.
|
||||
const { columns } = useKanbanGroups(
|
||||
rows,
|
||||
isGroupable ? property : undefined,
|
||||
@@ -41,6 +47,19 @@ export function BaseKanban({
|
||||
effectiveView?.config?.choiceOrder,
|
||||
);
|
||||
|
||||
const handlePickProperty = (propertyId: string) => {
|
||||
if (!effectiveView) return;
|
||||
updateViewMutation.mutate({
|
||||
viewId: effectiveView.id,
|
||||
pageId: base.id,
|
||||
config: { ...effectiveView.config, groupByPropertyId: propertyId },
|
||||
});
|
||||
};
|
||||
|
||||
if (!isGroupable) {
|
||||
return <KanbanEmptyState base={base} onPick={handlePickProperty} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.board}>
|
||||
{columns.map((column) => (
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Stack, Text } from "@mantine/core";
|
||||
import { IconColumns3 } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IBase } from "@/features/base/types/base.types";
|
||||
import { KanbanGroupByPicker } from "./kanban-group-by-picker";
|
||||
import { CreatePropertyPopover } from "@/features/base/components/property/create-property-popover";
|
||||
|
||||
type KanbanEmptyStateProps = {
|
||||
base: IBase;
|
||||
onPick: (propertyId: string) => void;
|
||||
};
|
||||
|
||||
export function KanbanEmptyState({ base, onPick }: KanbanEmptyStateProps) {
|
||||
const { t } = useTranslation();
|
||||
const hasGroupable = base.properties.some(
|
||||
(p) => p.type === "select" || p.type === "status",
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack align="center" gap="md" p="xl" mt={48}>
|
||||
<IconColumns3 size={48} color="var(--mantine-color-gray-5)" />
|
||||
<Text size="lg" fw={500}>
|
||||
{t("Choose a property to group by")}
|
||||
</Text>
|
||||
{hasGroupable ? (
|
||||
<KanbanGroupByPicker
|
||||
properties={base.properties}
|
||||
value={null}
|
||||
onChange={onPick}
|
||||
/>
|
||||
) : (
|
||||
<Stack align="center" gap="xs">
|
||||
<Text size="sm" c="dimmed">
|
||||
{t("Create a select or status property to use the kanban view.")}
|
||||
</Text>
|
||||
<CreatePropertyPopover
|
||||
pageId={base.id}
|
||||
properties={base.properties}
|
||||
onPropertyCreated={() => {
|
||||
// The base query invalidates on property create — the empty
|
||||
// state will re-render with the picker variant. The user
|
||||
// then picks the new property explicitly. (Auto-picking the
|
||||
// new property requires receiving its id from the create
|
||||
// mutation, which the current popover doesn't expose. Keep
|
||||
// the explicit step for now.)
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useMemo } from "react";
|
||||
import { Select } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IBaseProperty } from "@/features/base/types/base.types";
|
||||
|
||||
type KanbanGroupByPickerProps = {
|
||||
properties: IBaseProperty[];
|
||||
value: string | null;
|
||||
onChange: (propertyId: string) => void;
|
||||
// Allows the toolbar variant to render compact / narrow.
|
||||
size?: "xs" | "sm" | "md";
|
||||
};
|
||||
|
||||
export function KanbanGroupByPicker({
|
||||
properties,
|
||||
value,
|
||||
onChange,
|
||||
size = "sm",
|
||||
}: KanbanGroupByPickerProps) {
|
||||
const { t } = useTranslation();
|
||||
const data = useMemo(
|
||||
() =>
|
||||
properties
|
||||
.filter((p) => p.type === "select" || p.type === "status")
|
||||
.map((p) => ({ value: p.id, label: p.name || t("Untitled") })),
|
||||
[properties, t],
|
||||
);
|
||||
return (
|
||||
<Select
|
||||
placeholder={t("Group by…")}
|
||||
data={data}
|
||||
value={value}
|
||||
onChange={(v) => v && onChange(v)}
|
||||
size={size}
|
||||
allowDeselect={false}
|
||||
searchable
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user