mirror of
https://github.com/umami-software/umami.git
synced 2026-02-07 14:17:13 +01:00
Decompose BoardPage into individual components and remove debug logging.
Extract BoardRow, BoardColumn, BoardViewHeader, BoardEditHeader, and boardConstants into separate files. Remove 9 console.log statements from BoardBody and BoardProvider. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5f404f62d8
commit
18702e130e
8 changed files with 311 additions and 310 deletions
|
|
@ -74,9 +74,7 @@ export function BoardProvider({
|
|||
|
||||
// Get current layout sizes from BoardBody if registered
|
||||
const layoutData = layoutGetterRef.current?.();
|
||||
console.log('layoutData from getter:', layoutData);
|
||||
const parameters = layoutData ? { ...board.parameters, ...layoutData } : board.parameters;
|
||||
console.log('parameters to save:', parameters);
|
||||
|
||||
const result = await mutateAsync({
|
||||
...board,
|
||||
|
|
|
|||
|
|
@ -1,24 +1,12 @@
|
|||
import { Box, Button, Column, Icon, Row, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
||||
import { Button, Icon, Row, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
||||
import { produce } from 'immer';
|
||||
import { Fragment, type ReactElement, useEffect, useRef } from 'react';
|
||||
import { Fragment, useEffect, useRef } from 'react';
|
||||
import { Group, type GroupImperativeHandle, Panel, Separator } from 'react-resizable-panels';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { useBoard } from '@/components/hooks';
|
||||
import { ChevronDown, Minus, Plus, X } from '@/components/icons';
|
||||
import type { BoardColumn as BoardColumnType } from '@/lib/types';
|
||||
|
||||
const CATALOG = {
|
||||
text: {
|
||||
label: 'Text',
|
||||
component: BoardColumn,
|
||||
},
|
||||
};
|
||||
|
||||
const MIN_ROW_HEIGHT = 300;
|
||||
const MAX_ROW_HEIGHT = 600;
|
||||
const MIN_COLUMN_WIDTH = 300;
|
||||
const BUTTON_ROW_HEIGHT = 60;
|
||||
const MAX_COLUMNS = 4;
|
||||
import { Plus } from '@/components/icons';
|
||||
import { BoardRow } from './BoardRow';
|
||||
import { BUTTON_ROW_HEIGHT, MAX_ROW_HEIGHT, MIN_ROW_HEIGHT } from './boardConstants';
|
||||
|
||||
export function BoardBody() {
|
||||
const { board, editing, updateBoard, saveBoard, isPending, registerLayoutGetter } = useBoard();
|
||||
|
|
@ -29,19 +17,14 @@ export function BoardBody() {
|
|||
useEffect(() => {
|
||||
registerLayoutGetter(() => {
|
||||
const rows = board?.parameters?.rows;
|
||||
console.log('Layout getter called, rows:', rows);
|
||||
console.log('rowGroupRef.current:', rowGroupRef.current);
|
||||
console.log('columnGroupRefs.current:', columnGroupRefs.current);
|
||||
|
||||
if (!rows?.length) return null;
|
||||
|
||||
const rowLayout = rowGroupRef.current?.getLayout();
|
||||
console.log('rowLayout:', rowLayout);
|
||||
|
||||
const updatedRows = rows.map(row => {
|
||||
const columnGroupRef = columnGroupRefs.current.get(row.id);
|
||||
const columnLayout = columnGroupRef?.getLayout();
|
||||
console.log(`Row ${row.id} columnLayout:`, columnLayout);
|
||||
|
||||
const updatedColumns = row.columns.map(col => ({
|
||||
...col,
|
||||
|
|
@ -55,7 +38,6 @@ export function BoardBody() {
|
|||
};
|
||||
});
|
||||
|
||||
console.log('updatedRows:', updatedRows);
|
||||
return { rows: updatedRows };
|
||||
});
|
||||
}, [registerLayoutGetter, board?.parameters?.rows]);
|
||||
|
|
@ -68,8 +50,6 @@ export function BoardBody() {
|
|||
}
|
||||
};
|
||||
|
||||
console.log({ board });
|
||||
|
||||
const handleAddRow = () => {
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
|
|
@ -168,167 +148,3 @@ export function BoardBody() {
|
|||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
function BoardRow({
|
||||
rowId,
|
||||
rowIndex,
|
||||
rowCount,
|
||||
columns,
|
||||
editing = false,
|
||||
onRemove,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRegisterRef,
|
||||
}: {
|
||||
rowId: string;
|
||||
rowIndex: number;
|
||||
rowCount: number;
|
||||
columns: BoardColumnType[];
|
||||
editing?: boolean;
|
||||
onRemove?: (id: string) => void;
|
||||
onMoveUp?: (id: string) => void;
|
||||
onMoveDown?: (id: string) => void;
|
||||
onRegisterRef?: (rowId: string, ref: GroupImperativeHandle | null) => void;
|
||||
}) {
|
||||
const { board, updateBoard } = useBoard();
|
||||
|
||||
const handleGroupRef = (ref: GroupImperativeHandle | null) => {
|
||||
onRegisterRef?.(rowId, ref);
|
||||
};
|
||||
|
||||
const handleAddColumn = () => {
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
const rowIndex = draft.rows.findIndex(row => row.id === rowId);
|
||||
const row = draft.rows[rowIndex];
|
||||
|
||||
if (!row) {
|
||||
draft.rows[rowIndex] = { id: uuid(), columns: [] };
|
||||
}
|
||||
row.columns.push({ id: uuid(), component: null });
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveColumn = (columnId: string) => {
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
const row = draft.rows.find(row => row.id === rowId);
|
||||
if (row) {
|
||||
row.columns = row.columns.filter(col => col.id !== columnId);
|
||||
}
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Group groupRef={handleGroupRef} style={{ height: '100%' }}>
|
||||
{columns?.map((column, index) => (
|
||||
<Fragment key={column.id}>
|
||||
<Panel id={column.id} minSize={MIN_COLUMN_WIDTH} defaultSize={column.size}>
|
||||
<BoardColumn
|
||||
{...column}
|
||||
editing={editing}
|
||||
onRemove={handleRemoveColumn}
|
||||
canRemove={columns?.length > 1}
|
||||
/>
|
||||
</Panel>
|
||||
{index < columns?.length - 1 && <Separator />}
|
||||
</Fragment>
|
||||
))}
|
||||
{editing && (
|
||||
<Column alignSelf="center" padding="3" gap="1">
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="outline" onPress={() => onMoveUp?.(rowId)} isDisabled={rowIndex === 0}>
|
||||
<Icon rotate={180}>
|
||||
<ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="top">Move row up</Tooltip>
|
||||
</TooltipTrigger>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onPress={handleAddColumn}
|
||||
isDisabled={columns?.length >= MAX_COLUMNS}
|
||||
>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="left">Add column</Tooltip>
|
||||
</TooltipTrigger>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="outline" onPress={() => onRemove?.(rowId)}>
|
||||
<Icon>
|
||||
<Minus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="left">Remove row</Tooltip>
|
||||
</TooltipTrigger>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onPress={() => onMoveDown?.(rowId)}
|
||||
isDisabled={rowIndex === rowCount - 1}
|
||||
>
|
||||
<Icon>
|
||||
<ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="bottom">Move row down</Tooltip>
|
||||
</TooltipTrigger>
|
||||
</Column>
|
||||
)}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
function BoardColumn({
|
||||
id,
|
||||
component,
|
||||
editing = false,
|
||||
onRemove,
|
||||
canRemove = true,
|
||||
}: {
|
||||
id: string;
|
||||
component?: ReactElement;
|
||||
editing?: boolean;
|
||||
onRemove?: (id: string) => void;
|
||||
canRemove?: boolean;
|
||||
}) {
|
||||
const handleAddComponent = () => {};
|
||||
|
||||
return (
|
||||
<Column
|
||||
marginTop="3"
|
||||
marginLeft="3"
|
||||
width="100%"
|
||||
height="100%"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
backgroundColor="surface-sunken"
|
||||
position="relative"
|
||||
>
|
||||
{editing && canRemove && (
|
||||
<Box position="absolute" top="10px" right="20px" zIndex={100}>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="quiet" onPress={() => onRemove?.(id)}>
|
||||
<Icon size="sm">
|
||||
<X />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip>Remove column</Tooltip>
|
||||
</TooltipTrigger>
|
||||
</Box>
|
||||
)}
|
||||
{editing && (
|
||||
<Button variant="outline" onPress={handleAddComponent}>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
52
src/app/(main)/boards/[boardId]/BoardColumn.tsx
Normal file
52
src/app/(main)/boards/[boardId]/BoardColumn.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { Box, Button, Column, Icon, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
||||
import type { ReactElement } from 'react';
|
||||
import { Plus, X } from '@/components/icons';
|
||||
|
||||
export function BoardColumn({
|
||||
id,
|
||||
component,
|
||||
editing = false,
|
||||
onRemove,
|
||||
canRemove = true,
|
||||
}: {
|
||||
id: string;
|
||||
component?: ReactElement;
|
||||
editing?: boolean;
|
||||
onRemove?: (id: string) => void;
|
||||
canRemove?: boolean;
|
||||
}) {
|
||||
const handleAddComponent = () => {};
|
||||
|
||||
return (
|
||||
<Column
|
||||
marginTop="3"
|
||||
marginLeft="3"
|
||||
width="100%"
|
||||
height="100%"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
backgroundColor="surface-sunken"
|
||||
position="relative"
|
||||
>
|
||||
{editing && canRemove && (
|
||||
<Box position="absolute" top="10px" right="20px" zIndex={100}>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="quiet" onPress={() => onRemove?.(id)}>
|
||||
<Icon size="sm">
|
||||
<X />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip>Remove column</Tooltip>
|
||||
</TooltipTrigger>
|
||||
</Box>
|
||||
)}
|
||||
{editing && (
|
||||
<Button variant="outline" onPress={handleAddComponent}>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
99
src/app/(main)/boards/[boardId]/BoardEditHeader.tsx
Normal file
99
src/app/(main)/boards/[boardId]/BoardEditHeader.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import {
|
||||
Button,
|
||||
Column,
|
||||
Grid,
|
||||
Heading,
|
||||
LoadingButton,
|
||||
Row,
|
||||
Text,
|
||||
TextField,
|
||||
} from '@umami/react-zen';
|
||||
import { useBoard, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
||||
|
||||
export function BoardEditHeader() {
|
||||
const { board, updateBoard, saveBoard, isPending } = useBoard();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { router, renderUrl } = useNavigation();
|
||||
const defaultName = formatMessage(labels.untitled);
|
||||
|
||||
const handleNameChange = (value: string) => {
|
||||
updateBoard({ name: value });
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (value: string) => {
|
||||
updateBoard({ description: value });
|
||||
};
|
||||
|
||||
const handleWebsiteChange = (websiteId: string) => {
|
||||
updateBoard({ parameters: { ...board.parameters, websiteId } });
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
await saveBoard();
|
||||
if (board.id) {
|
||||
router.push(renderUrl(`/boards/${board.id}`));
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
if (board.id) {
|
||||
router.push(renderUrl(`/boards/${board.id}`));
|
||||
} else {
|
||||
router.push(renderUrl('/boards'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid
|
||||
columns={{ base: '1fr', md: '1fr 1fr' }}
|
||||
paddingY="4"
|
||||
marginBottom="6"
|
||||
border="bottom"
|
||||
gapX="6"
|
||||
>
|
||||
<Column>
|
||||
<Row>
|
||||
<TextField
|
||||
variant="quiet"
|
||||
name="name"
|
||||
value={board?.name ?? ''}
|
||||
placeholder={defaultName}
|
||||
onChange={handleNameChange}
|
||||
autoComplete="off"
|
||||
style={{ fontSize: '2rem', fontWeight: 700, width: '100%' }}
|
||||
>
|
||||
<Heading size="xl">{board?.name}</Heading>
|
||||
</TextField>
|
||||
</Row>
|
||||
<Row>
|
||||
<TextField
|
||||
variant="quiet"
|
||||
name="description"
|
||||
value={board?.description ?? ''}
|
||||
placeholder={`+ ${formatMessage(labels.addDescription)}`}
|
||||
autoComplete="off"
|
||||
onChange={handleDescriptionChange}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{board?.description}
|
||||
</TextField>
|
||||
</Row>
|
||||
<Row alignItems="center" gap="3">
|
||||
<Text>{formatMessage(labels.website)}</Text>
|
||||
<WebsiteSelect websiteId={board?.parameters?.websiteId} onChange={handleWebsiteChange} />
|
||||
</Row>
|
||||
</Column>
|
||||
<Column justifyContent="center" alignItems="flex-end">
|
||||
<Row gap="3">
|
||||
<Button variant="quiet" onPress={handleCancel}>
|
||||
{formatMessage(labels.cancel)}
|
||||
</Button>
|
||||
<LoadingButton variant="primary" onPress={handleSave} isLoading={isPending}>
|
||||
{formatMessage(labels.save)}
|
||||
</LoadingButton>
|
||||
</Row>
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,19 +1,6 @@
|
|||
import {
|
||||
Button,
|
||||
Column,
|
||||
Grid,
|
||||
Heading,
|
||||
LoadingButton,
|
||||
Row,
|
||||
Text,
|
||||
TextField,
|
||||
} from '@umami/react-zen';
|
||||
import { IconLabel } from '@/components/common/IconLabel';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { useBoard, useMessages, useNavigation, useWebsiteQuery } from '@/components/hooks';
|
||||
import { Edit } from '@/components/icons';
|
||||
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
||||
import { useBoard } from '@/components/hooks';
|
||||
import { BoardEditHeader } from './BoardEditHeader';
|
||||
import { BoardViewHeader } from './BoardViewHeader';
|
||||
|
||||
export function BoardHeader() {
|
||||
const { board, editing } = useBoard();
|
||||
|
|
@ -24,106 +11,3 @@ export function BoardHeader() {
|
|||
|
||||
return <BoardViewHeader />;
|
||||
}
|
||||
|
||||
function BoardViewHeader() {
|
||||
const { board } = useBoard();
|
||||
const { renderUrl } = useNavigation();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { data: website } = useWebsiteQuery(board?.parameters?.websiteId);
|
||||
|
||||
return (
|
||||
<PageHeader title={board?.name} description={board?.description}>
|
||||
{website?.name && <Text>{website.name}</Text>}
|
||||
<LinkButton href={renderUrl(`/boards/${board?.id}/edit`, false)}>
|
||||
<IconLabel icon={<Edit />}>{formatMessage(labels.edit)}</IconLabel>
|
||||
</LinkButton>
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
|
||||
function BoardEditHeader() {
|
||||
const { board, updateBoard, saveBoard, isPending } = useBoard();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { router, renderUrl } = useNavigation();
|
||||
const defaultName = formatMessage(labels.untitled);
|
||||
|
||||
const handleNameChange = (value: string) => {
|
||||
updateBoard({ name: value });
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (value: string) => {
|
||||
updateBoard({ description: value });
|
||||
};
|
||||
|
||||
const handleWebsiteChange = (websiteId: string) => {
|
||||
updateBoard({ parameters: { ...board.parameters, websiteId } });
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
await saveBoard();
|
||||
if (board.id) {
|
||||
router.push(renderUrl(`/boards/${board.id}`));
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
if (board.id) {
|
||||
router.push(renderUrl(`/boards/${board.id}`));
|
||||
} else {
|
||||
router.push(renderUrl('/boards'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid
|
||||
columns={{ base: '1fr', md: '1fr 1fr' }}
|
||||
paddingY="4"
|
||||
marginBottom="6"
|
||||
border="bottom"
|
||||
gapX="6"
|
||||
>
|
||||
<Column>
|
||||
<Row>
|
||||
<TextField
|
||||
variant="quiet"
|
||||
name="name"
|
||||
value={board?.name ?? ''}
|
||||
placeholder={defaultName}
|
||||
onChange={handleNameChange}
|
||||
autoComplete="off"
|
||||
style={{ fontSize: '2rem', fontWeight: 700, width: '100%' }}
|
||||
>
|
||||
<Heading size="xl">{board?.name}</Heading>
|
||||
</TextField>
|
||||
</Row>
|
||||
<Row>
|
||||
<TextField
|
||||
variant="quiet"
|
||||
name="description"
|
||||
value={board?.description ?? ''}
|
||||
placeholder={`+ ${formatMessage(labels.addDescription)}`}
|
||||
autoComplete="off"
|
||||
onChange={handleDescriptionChange}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{board?.description}
|
||||
</TextField>
|
||||
</Row>
|
||||
<Row alignItems="center" gap="3">
|
||||
<Text>{formatMessage(labels.website)}</Text>
|
||||
<WebsiteSelect websiteId={board?.parameters?.websiteId} onChange={handleWebsiteChange} />
|
||||
</Row>
|
||||
</Column>
|
||||
<Column justifyContent="center" alignItems="flex-end">
|
||||
<Row gap="3">
|
||||
<Button variant="quiet" onPress={handleCancel}>
|
||||
{formatMessage(labels.cancel)}
|
||||
</Button>
|
||||
<LoadingButton variant="primary" onPress={handleSave} isLoading={isPending}>
|
||||
{formatMessage(labels.save)}
|
||||
</LoadingButton>
|
||||
</Row>
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
125
src/app/(main)/boards/[boardId]/BoardRow.tsx
Normal file
125
src/app/(main)/boards/[boardId]/BoardRow.tsx
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import { Button, Column, Icon, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
||||
import { produce } from 'immer';
|
||||
import { Fragment } from 'react';
|
||||
import { Group, type GroupImperativeHandle, Panel, Separator } from 'react-resizable-panels';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { useBoard } from '@/components/hooks';
|
||||
import { ChevronDown, Minus, Plus } from '@/components/icons';
|
||||
import type { BoardColumn as BoardColumnType } from '@/lib/types';
|
||||
import { BoardColumn } from './BoardColumn';
|
||||
import { MAX_COLUMNS, MIN_COLUMN_WIDTH } from './boardConstants';
|
||||
|
||||
export function BoardRow({
|
||||
rowId,
|
||||
rowIndex,
|
||||
rowCount,
|
||||
columns,
|
||||
editing = false,
|
||||
onRemove,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRegisterRef,
|
||||
}: {
|
||||
rowId: string;
|
||||
rowIndex: number;
|
||||
rowCount: number;
|
||||
columns: BoardColumnType[];
|
||||
editing?: boolean;
|
||||
onRemove?: (id: string) => void;
|
||||
onMoveUp?: (id: string) => void;
|
||||
onMoveDown?: (id: string) => void;
|
||||
onRegisterRef?: (rowId: string, ref: GroupImperativeHandle | null) => void;
|
||||
}) {
|
||||
const { board, updateBoard } = useBoard();
|
||||
|
||||
const handleGroupRef = (ref: GroupImperativeHandle | null) => {
|
||||
onRegisterRef?.(rowId, ref);
|
||||
};
|
||||
|
||||
const handleAddColumn = () => {
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
const rowIndex = draft.rows.findIndex(row => row.id === rowId);
|
||||
const row = draft.rows[rowIndex];
|
||||
|
||||
if (!row) {
|
||||
draft.rows[rowIndex] = { id: uuid(), columns: [] };
|
||||
}
|
||||
row.columns.push({ id: uuid(), component: null });
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveColumn = (columnId: string) => {
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
const row = draft.rows.find(row => row.id === rowId);
|
||||
if (row) {
|
||||
row.columns = row.columns.filter(col => col.id !== columnId);
|
||||
}
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Group groupRef={handleGroupRef} style={{ height: '100%' }}>
|
||||
{columns?.map((column, index) => (
|
||||
<Fragment key={column.id}>
|
||||
<Panel id={column.id} minSize={MIN_COLUMN_WIDTH} defaultSize={column.size}>
|
||||
<BoardColumn
|
||||
{...column}
|
||||
editing={editing}
|
||||
onRemove={handleRemoveColumn}
|
||||
canRemove={columns?.length > 1}
|
||||
/>
|
||||
</Panel>
|
||||
{index < columns?.length - 1 && <Separator />}
|
||||
</Fragment>
|
||||
))}
|
||||
{editing && (
|
||||
<Column alignSelf="center" padding="3" gap="1">
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="outline" onPress={() => onMoveUp?.(rowId)} isDisabled={rowIndex === 0}>
|
||||
<Icon rotate={180}>
|
||||
<ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="top">Move row up</Tooltip>
|
||||
</TooltipTrigger>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onPress={handleAddColumn}
|
||||
isDisabled={columns?.length >= MAX_COLUMNS}
|
||||
>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="left">Add column</Tooltip>
|
||||
</TooltipTrigger>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="outline" onPress={() => onRemove?.(rowId)}>
|
||||
<Icon>
|
||||
<Minus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="left">Remove row</Tooltip>
|
||||
</TooltipTrigger>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onPress={() => onMoveDown?.(rowId)}
|
||||
isDisabled={rowIndex === rowCount - 1}
|
||||
>
|
||||
<Icon>
|
||||
<ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="bottom">Move row down</Tooltip>
|
||||
</TooltipTrigger>
|
||||
</Column>
|
||||
)}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
22
src/app/(main)/boards/[boardId]/BoardViewHeader.tsx
Normal file
22
src/app/(main)/boards/[boardId]/BoardViewHeader.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { Text } from '@umami/react-zen';
|
||||
import { IconLabel } from '@/components/common/IconLabel';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { useBoard, useMessages, useNavigation, useWebsiteQuery } from '@/components/hooks';
|
||||
import { Edit } from '@/components/icons';
|
||||
|
||||
export function BoardViewHeader() {
|
||||
const { board } = useBoard();
|
||||
const { renderUrl } = useNavigation();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { data: website } = useWebsiteQuery(board?.parameters?.websiteId);
|
||||
|
||||
return (
|
||||
<PageHeader title={board?.name} description={board?.description}>
|
||||
{website?.name && <Text>{website.name}</Text>}
|
||||
<LinkButton href={renderUrl(`/boards/${board?.id}/edit`, false)}>
|
||||
<IconLabel icon={<Edit />}>{formatMessage(labels.edit)}</IconLabel>
|
||||
</LinkButton>
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
5
src/app/(main)/boards/[boardId]/boardConstants.ts
Normal file
5
src/app/(main)/boards/[boardId]/boardConstants.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export const MIN_ROW_HEIGHT = 300;
|
||||
export const MAX_ROW_HEIGHT = 600;
|
||||
export const MIN_COLUMN_WIDTH = 300;
|
||||
export const BUTTON_ROW_HEIGHT = 60;
|
||||
export const MAX_COLUMNS = 4;
|
||||
Loading…
Add table
Add a link
Reference in a new issue