mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 21:57:16 +01:00
Board editing.
This commit is contained in:
parent
68c56060b3
commit
d9f698ca42
8 changed files with 183 additions and 86 deletions
|
|
@ -3,7 +3,7 @@ import { Loading, useToast } from '@umami/react-zen';
|
|||
import { createContext, type ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import { useApi, useMessages, useModified, useNavigation } from '@/components/hooks';
|
||||
import { useBoardQuery } from '@/components/hooks/queries/useBoardQuery';
|
||||
import type { Board } from '@/generated/prisma/client';
|
||||
import type { Board } from '@/lib/types';
|
||||
|
||||
export interface BoardContextValue {
|
||||
board: Partial<Board>;
|
||||
|
|
@ -17,6 +17,7 @@ export const BoardContext = createContext<BoardContextValue>(null);
|
|||
const defaultBoard: Partial<Board> = {
|
||||
name: '',
|
||||
description: '',
|
||||
parameters: { rows: [] },
|
||||
};
|
||||
|
||||
export function BoardProvider({ boardId, children }: { boardId?: string; children: ReactNode }) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,18 @@
|
|||
import { Button, Column, Icon, Row } from '@umami/react-zen';
|
||||
import { Box, Button, Column, Icon, Row, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
||||
import { produce } from 'immer';
|
||||
import { Fragment, type ReactElement } from 'react';
|
||||
import { Group, Panel, Separator } from 'react-resizable-panels';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { useBoard } from '@/components/hooks';
|
||||
import { Plus } from '@/components/icons';
|
||||
import { Minus, Plus } from '@/components/icons';
|
||||
import type { BoardColumn as BoardColumnType } from '@/lib/types';
|
||||
|
||||
const CATALOG = {
|
||||
text: {
|
||||
label: 'Text',
|
||||
component: BoardColumn,
|
||||
},
|
||||
};
|
||||
|
||||
export function BoardBody() {
|
||||
const { board, updateBoard, saveBoard, isPending } = useBoard();
|
||||
|
|
@ -15,55 +25,126 @@ export function BoardBody() {
|
|||
if (!draft.rows) {
|
||||
draft.rows = [];
|
||||
}
|
||||
draft.rows.push({ id: uuid(), components: [] });
|
||||
draft.rows.push({ id: uuid(), columns: [{ id: uuid(), component: null }] });
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveRow = (id: string) => {
|
||||
console.log('Removing row', id);
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
if (!draft.rows) {
|
||||
return;
|
||||
}
|
||||
|
||||
draft.rows = draft.rows.filter(row => row?.id !== id);
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const rows = board?.parameters?.rows ?? [];
|
||||
const minHeight = 300 * (rows.length || 1);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Group orientation="vertical" style={{ minHeight }}>
|
||||
{rows.map((row, index) => (
|
||||
<Fragment key={row.id}>
|
||||
<Panel minSize={200}>
|
||||
<BoardRow {...row} rowId={row.id} onRemove={handleRemoveRow} />
|
||||
</Panel>
|
||||
{index < rows.length - 1 && <Separator />}
|
||||
</Fragment>
|
||||
))}
|
||||
</Group>
|
||||
<Row>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="outline" onPress={handleAddRow}>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="bottom">Add row</Tooltip>
|
||||
</TooltipTrigger>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BoardRow({
|
||||
rowId,
|
||||
columns,
|
||||
onRemove,
|
||||
}: {
|
||||
rowId: string;
|
||||
columns: BoardColumnType[];
|
||||
onAddComponent?: () => void;
|
||||
onRemove?: (id: string) => void;
|
||||
}) {
|
||||
const { board, updateBoard } = useBoard();
|
||||
|
||||
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 });
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Column>
|
||||
{board?.parameters?.rows?.map((row, rowIndex) => {
|
||||
return <BoardRow key={row.id} rowIndex={rowIndex} components={row.components} />;
|
||||
})}
|
||||
<Row>
|
||||
<Button variant="outline" onPress={handleAddRow}>
|
||||
<Group style={{ height: '100%' }}>
|
||||
{columns?.map((column, index) => (
|
||||
<Fragment key={column.id}>
|
||||
<Panel minSize={300}>
|
||||
<BoardColumn {...column} />
|
||||
</Panel>
|
||||
{index < columns.length - 1 && <Separator />}
|
||||
</Fragment>
|
||||
))}
|
||||
<Box alignSelf="center" padding="3">
|
||||
<Button variant="outline" onPress={handleAddColumn}>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
</Row>
|
||||
</Column>
|
||||
<TooltipTrigger delay={0}>
|
||||
<Button variant="outline" onPress={() => onRemove?.(rowId)}>
|
||||
<Icon>
|
||||
<Minus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Tooltip placement="bottom">Remove row</Tooltip>
|
||||
</TooltipTrigger>
|
||||
</Box>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
function BoardComponent() {
|
||||
return <Column>hi</Column>;
|
||||
}
|
||||
|
||||
function BoardRow({ rowIndex, components }: { rowIndex: number; components: any[] }) {
|
||||
const { board, updateBoard } = useBoard();
|
||||
|
||||
const handleAddComponent = () => {
|
||||
updateBoard({
|
||||
parameters: produce(board.parameters, draft => {
|
||||
if (!draft.rows[rowIndex]) {
|
||||
draft.rows[rowIndex] = { id: uuid(), components: [] };
|
||||
}
|
||||
draft.rows[rowIndex].components.push({ id: uuid(), type: 'text', value: '' });
|
||||
}),
|
||||
});
|
||||
};
|
||||
function BoardColumn({ id, component }: { id: string; component?: ReactElement }) {
|
||||
const handleAddComponent = () => {};
|
||||
|
||||
return (
|
||||
<Row>
|
||||
{components?.map(component => {
|
||||
return <BoardComponent key={component.id} />;
|
||||
})}
|
||||
<Column
|
||||
marginTop="3"
|
||||
marginLeft="3"
|
||||
width="100%"
|
||||
height="100%"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
backgroundColor="3"
|
||||
>
|
||||
<Button variant="outline" onPress={handleAddComponent}>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { z } from 'zod';
|
||||
import { SHARE_ID_REGEX } from '@/lib/constants';
|
||||
import { parseRequest } from '@/lib/request';
|
||||
import { badRequest, json, ok, serverError, unauthorized } from '@/lib/response';
|
||||
import { canDeleteBoard, canUpdateBoard, canViewBoard } from '@/permissions';
|
||||
|
|
@ -27,7 +26,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ boa
|
|||
const schema = z.object({
|
||||
name: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
shareId: z.string().regex(SHARE_ID_REGEX).nullable().optional(),
|
||||
parameters: z.object({}).passthrough().optional(),
|
||||
});
|
||||
|
||||
const { auth, body, error } = await parseRequest(request, schema);
|
||||
|
|
@ -37,14 +36,14 @@ export async function POST(request: Request, { params }: { params: Promise<{ boa
|
|||
}
|
||||
|
||||
const { boardId } = await params;
|
||||
const { name, description, shareId } = body;
|
||||
const { name, description, parameters } = body;
|
||||
|
||||
if (!(await canUpdateBoard(auth, boardId))) {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
try {
|
||||
const board = await updateBoard(boardId, { name, description, shareId });
|
||||
const board = await updateBoard(boardId, { name, description, parameters });
|
||||
|
||||
return Response.json(board);
|
||||
} catch (e: any) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import type { UseQueryOptions } from '@tanstack/react-query';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { Board as PrismaBoard } from '@/generated/prisma/client';
|
||||
import type { DATA_TYPE, OPERATORS, ROLES } from './constants';
|
||||
import type { TIME_UNIT } from './date';
|
||||
|
||||
|
|
@ -142,6 +144,26 @@ export interface ApiError extends Error {
|
|||
message: string;
|
||||
}
|
||||
|
||||
export interface BoardData {
|
||||
rows: { id: string; name: string; value: number }[];
|
||||
export interface BoardComponent {
|
||||
id: string;
|
||||
type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface BoardColumn {
|
||||
id: string;
|
||||
component?: ReactElement;
|
||||
}
|
||||
|
||||
export interface BoardRow {
|
||||
id: string;
|
||||
columns: BoardColumn[];
|
||||
}
|
||||
|
||||
export interface BoardParameters {
|
||||
rows?: BoardRow[];
|
||||
}
|
||||
|
||||
export interface Board extends Omit<PrismaBoard, 'parameters'> {
|
||||
parameters: BoardParameters;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue