diff --git a/apps/frontend/schema.graphql b/apps/frontend/schema.graphql index 9c5a41554..a40d055a3 100644 --- a/apps/frontend/schema.graphql +++ b/apps/frontend/schema.graphql @@ -75,6 +75,10 @@ type GalleryOption { field: String } +type GridOption { + widths: JSON +} + type Invitation { email: String! id: ID! @@ -204,6 +208,7 @@ type View { fields: JSON filter: JSON gallery: GalleryOption + grid: GridOption id: ID! isDefault: Boolean kanban: KanbanOption diff --git a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql index e7ba5bdd6..9be009854 100644 --- a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql +++ b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql @@ -27,6 +27,10 @@ query GetTableQuery($tableId: ID!, $viewId: ID) { aggregate fields + grid { + widths + } + kanban { field } diff --git a/apps/frontend/src/routes/(share)/s/b/[shareId]/+layout.gql b/apps/frontend/src/routes/(share)/s/b/[shareId]/+layout.gql index 7e074ce65..93f822835 100644 --- a/apps/frontend/src/routes/(share)/s/b/[shareId]/+layout.gql +++ b/apps/frontend/src/routes/(share)/s/b/[shareId]/+layout.gql @@ -31,6 +31,9 @@ query GetShareBaseData($shareId: ID!) { name type isDefault + grid { + widths + } kanban { field } diff --git a/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.gql b/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.gql index d0dd6d470..f071783bb 100644 --- a/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.gql +++ b/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.gql @@ -27,6 +27,9 @@ query GetBaseTableShareData($shareId: ID!, $tableId: ID!) { isDefault fields type + grid { + widths + } kanban { field } diff --git a/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.gql b/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.gql index 7c9be6233..2e7ba163f 100644 --- a/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.gql +++ b/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.gql @@ -38,6 +38,9 @@ query GetViewShareData($shareId: ID!) { option { showSystemFields } + grid { + widths + } kanban { field } diff --git a/packages/authz/src/space-member/space-action.ts b/packages/authz/src/space-member/space-action.ts index fff1a78dd..8b4fc7a5d 100644 --- a/packages/authz/src/space-member/space-action.ts +++ b/packages/authz/src/space-member/space-action.ts @@ -18,6 +18,12 @@ export const spaceActions = z.enum([ "table:list", "table:delete", + "view:create", + "view:update", + "view:read", + "view:list", + "view:delete", + "form:create", "form:update", "form:list", diff --git a/packages/authz/src/space-member/space-permission.ts b/packages/authz/src/space-member/space-permission.ts index 2002504d8..7d5263279 100644 --- a/packages/authz/src/space-member/space-permission.ts +++ b/packages/authz/src/space-member/space-permission.ts @@ -14,6 +14,12 @@ export const spacePermission: Record { + public readonly logger = createLogger(SetFieldWidthCommandHandler.name) + constructor( + @injectTableRepository() + private readonly repo: ITableRepository, + ) {} + + async execute(command: SetFieldWidthCommand): Promise { + const table = (await this.repo.findOneById(new TableIdVo(command.tableId))).unwrap() + + const spec = table.$setFieldWidth(command) + + await this.repo.updateOneById(table, spec) + } +} diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 645e48f0e..d84d0adbf 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -31,6 +31,7 @@ export * from "./duplicate-view.command" export * from "./enable-share.command" export * from "./export-view.command" export * from "./invite.command" +export * from "./set-field-width.command" export * from "./set-table-form.command" export * from "./set-table-rls.command" export * from "./set-view-aggregate.command" diff --git a/packages/commands/src/set-field-width.command.ts b/packages/commands/src/set-field-width.command.ts new file mode 100644 index 000000000..652b83a7c --- /dev/null +++ b/packages/commands/src/set-field-width.command.ts @@ -0,0 +1,22 @@ +import { Command, type CommandProps } from "@undb/domain" +import { setFieldWidthDTO } from "@undb/table" +import { z } from "@undb/zod" + +export const setFieldWidthCommand = setFieldWidthDTO + +export type ISetFieldWidthCommand = z.infer + +export class SetFieldWidthCommand extends Command { + public readonly tableId: string + public readonly viewId?: string + public readonly field: string + public readonly width: number + + constructor(props: CommandProps) { + super(props) + this.tableId = props.tableId + this.viewId = props.viewId + this.field = props.field + this.width = props.width + } +} diff --git a/packages/graphql/src/index.ts b/packages/graphql/src/index.ts index 1c1fd2edb..d4d6763ca 100644 --- a/packages/graphql/src/index.ts +++ b/packages/graphql/src/index.ts @@ -131,6 +131,10 @@ export class Graphql { showSystemFields: Boolean } + type GridOption { + widths: JSON + } + type KanbanOption { field: String } @@ -151,6 +155,7 @@ export class Graphql { aggregate: JSON fields: JSON + grid: GridOption kanban: KanbanOption gallery: GalleryOption diff --git a/packages/table/src/dto/index.ts b/packages/table/src/dto/index.ts index da37d4f84..e6e1c85ed 100644 --- a/packages/table/src/dto/index.ts +++ b/packages/table/src/dto/index.ts @@ -6,6 +6,7 @@ export * from "./delete-view.dto" export * from "./duplicate-table-field.dto" export * from "./duplicate-table.dto" export * from "./duplicate-view.dto" +export * from "./set-field-width.dto" export * from "./set-table-form.dto" export * from "./set-table-rls.dto" export * from "./set-view-aggregate.dto" diff --git a/packages/table/src/dto/set-field-width.dto.ts b/packages/table/src/dto/set-field-width.dto.ts new file mode 100644 index 000000000..98c1a3534 --- /dev/null +++ b/packages/table/src/dto/set-field-width.dto.ts @@ -0,0 +1,13 @@ +import { z } from "@undb/zod" +import { viewId } from "../modules" +import { fieldId } from "../modules/schema/fields/field-id.vo" +import { tableId } from "../table-id.vo" + +export const setFieldWidthDTO = z.object({ + tableId: tableId, + viewId: viewId.optional(), + field: fieldId, + width: z.number(), +}) + +export type ISetFieldWidthDTO = z.infer diff --git a/packages/table/src/methods/set-field-width.method.ts b/packages/table/src/methods/set-field-width.method.ts new file mode 100644 index 000000000..51f968944 --- /dev/null +++ b/packages/table/src/methods/set-field-width.method.ts @@ -0,0 +1,14 @@ +import { type Option } from "@undb/domain" +import type { ISetFieldWidthDTO } from "../dto" +import type { TableComositeSpecification } from "../specifications" +import type { TableDo } from "../table.do" + +export function setFieldWidth(this: TableDo, dto: ISetFieldWidthDTO): Option { + const view = this.views.getViewById(dto.viewId) + + if (view.type !== "grid") { + throw new Error("View type is not grid") + } + + throw new Error("Not implemented") +} diff --git a/packages/table/src/modules/views/view/variants/grid-view.vo.ts b/packages/table/src/modules/views/view/variants/grid-view.vo.ts index b3bcf4aac..0ef6e872a 100644 --- a/packages/table/src/modules/views/view/variants/grid-view.vo.ts +++ b/packages/table/src/modules/views/view/variants/grid-view.vo.ts @@ -1,4 +1,4 @@ -import { Option, Some } from "@undb/domain" +import { None, Option, Some } from "@undb/domain" import { z } from "@undb/zod" import type { IDuplicateViewDTO } from "../../../../dto" import { WithNewView, WithView } from "../../../../specifications/table-view.specification" @@ -7,14 +7,22 @@ import { AbstractView, baseViewDTO, createBaseViewDTO, updateBaseViewDTO } from export const GRID_TYPE = "grid" as const +export const gridOption = z.object({ + widths: z.record(z.number().positive()), +}) + +export type IGridOption = z.infer + export const createGridViewDTO = createBaseViewDTO.extend({ type: z.literal(GRID_TYPE), + grid: gridOption.optional(), }) export type ICreateGridViewDTO = z.infer export const gridViewDTO = baseViewDTO.extend({ type: z.literal(GRID_TYPE), + grid: gridOption.optional(), }) export type IGridViewDTO = z.infer @@ -22,14 +30,17 @@ export type IGridViewDTO = z.infer export const updateGridViewDTO = updateBaseViewDTO.merge( z.object({ type: z.literal(GRID_TYPE), + grid: gridOption.optional(), }), ) export type IUpdateGridViewDTO = z.infer export class GridView extends AbstractView { + grid: Option = None constructor(dto: IGridViewDTO) { super(dto) + this.grid = Option(dto.grid) } static create(dto: ICreateGridViewDTO) { @@ -40,7 +51,13 @@ export class GridView extends AbstractView { override $update(input: IUpdateGridViewDTO): Option { const json = this.toJSON() - const view = new GridView({ ...json, name: input.name, id: this.id.value, type: GRID_TYPE }) + const view = new GridView({ + ...json, + name: input.name, + id: this.id.value, + type: GRID_TYPE, + grid: input.grid ?? this.grid.into(undefined), + }) return Some(new WithView(this, view)) } @@ -56,6 +73,7 @@ export class GridView extends AbstractView { isDefault: false, id: ViewIdVo.create().value, type: GRID_TYPE, + grid: this.grid.into(undefined), }), ), ) diff --git a/packages/table/src/table.do.ts b/packages/table/src/table.do.ts index e9e8c5de6..aef954468 100644 --- a/packages/table/src/table.do.ts +++ b/packages/table/src/table.do.ts @@ -11,6 +11,7 @@ import { deleteViewMethod } from "./methods/delete-view.method" import { duplicateFieldMethod } from "./methods/duplicate-field.method" import { duplicateTableMethod } from "./methods/duplicate-table.method" import { duplicateViewMethod } from "./methods/duplicate-view.method" +import { setFieldWidth } from "./methods/set-field-width.method" import { setTableForm } from "./methods/set-table-form.method" import { setTableRLS } from "./methods/set-table-rls.method" import { setViewAggregate } from "./methods/set-view-aggregate.method" @@ -55,6 +56,7 @@ export class TableDo extends AggregateRoot { $update = updateTable $duplicate = duplicateTableMethod $updateView = updateView + $setFieldWidth = setFieldWidth $setViewFilter = setViewFilter $setViewOption = setViewOption $setViewColor = setViewColor diff --git a/packages/trpc/src/router.ts b/packages/trpc/src/router.ts index 8f132192f..11a912235 100644 --- a/packages/trpc/src/router.ts +++ b/packages/trpc/src/router.ts @@ -29,6 +29,7 @@ import { DuplicateViewCommand, EnableShareCommand, InviteCommand, + SetFieldWidthCommand, SetTableFormCommand, SetTableRLSCommand, SetViewAggregateCommand, @@ -78,6 +79,7 @@ import { duplicateViewCommand, enableShareCommand, inviteCommand, + setFieldWidthCommand, setTableFormCommand, setTableRLSCommand, setViewAggregateCommand, @@ -153,35 +155,49 @@ const formRouter = t.router({ const viewRouter = t.router({ create: privateProcedure + .use(authz("view:create")) .input(createTableViewCommand) .mutation(({ input }) => commandBus.execute(new CreateTableViewCommand(input))), update: privateProcedure + .use(authz("view:update")) .input(updateViewCommand) .mutation(({ input }) => commandBus.execute(new UpdateViewCommand(input))), duplicate: privateProcedure + .use(authz("view:create")) .input(duplicateViewCommand) .mutation(({ input }) => commandBus.execute(new DuplicateViewCommand(input))), delete: privateProcedure + .use(authz("view:delete")) .input(deleteViewCommand) .mutation(({ input }) => commandBus.execute(new DeleteViewCommand(input))), setFilter: privateProcedure + .use(authz("view:update")) .input(setViewFilterCommand) .mutation(({ input }) => commandBus.execute(new SetViewFilterCommand(input))), setOption: privateProcedure + .use(authz("view:update")) .input(setViewOptionCommand) .mutation(({ input }) => commandBus.execute(new SetViewOptionCommand(input))), setColor: privateProcedure + .use(authz("view:update")) .input(setViewColorCommand) .mutation(({ input }) => commandBus.execute(new SetViewColorCommand(input))), setSort: privateProcedure + .use(authz("view:update")) .input(setViewSortCommand) .mutation(({ input }) => commandBus.execute(new SetViewSortCommand(input))), setAggregate: privateProcedure + .use(authz("view:update")) .input(setViewAggregateCommand) .mutation(({ input }) => commandBus.execute(new SetViewAggregateCommand(input))), setFields: privateProcedure + .use(authz("view:update")) .input(setViewFieldsCommand) .mutation(({ input }) => commandBus.execute(new SetViewFieldsCommand(input))), + setFieldWidth: privateProcedure + .use(authz("view:update")) + .input(setFieldWidthCommand) + .mutation(({ input }) => commandBus.execute(new SetFieldWidthCommand(input))), }) const rlsRouter = t.router({