Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,10 @@ export class FieldOpenApiV2Service {
context
);
const { hasAiConfig, nextAiConfig, v2Field } = preparedField;
const legacyViewId =
fieldRo && typeof fieldRo === 'object' && 'viewId' in fieldRo
? (fieldRo.viewId as string | undefined)
: undefined;
const legacyOrder =
fieldRo && typeof fieldRo === 'object' && 'order' in fieldRo
? (fieldRo.order as
Expand All @@ -1291,6 +1295,7 @@ export class FieldOpenApiV2Service {
baseId: table.baseId().toString(),
tableId,
field: v2Field,
...(typeof legacyViewId === 'string' ? { viewId: legacyViewId } : {}),
...(normalizedOrder ? { order: normalizedOrder } : {}),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ export const FieldSetting = (props: IFieldSetting) => {
let result: IFieldVo | undefined;
try {
if (operator === FieldOperator.Add) {
result = await createNewField(field);
result = await createNewField({
...field,
viewId: view?.id,
});
}

if (operator === FieldOperator.Insert) {
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/models/field/field.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ export const createFieldRoSchema = baseFieldRoSchema
'The id of the field that start with "fld", followed by exactly 16 alphanumeric characters `/^fld[\\da-zA-Z]{16}$/`. It is sometimes useful to specify an id at creation time',
example: 'fldxxxxxxxxxxxxxxxx',
}),
viewId: z.string().startsWith(IdPrefix.View).optional().meta({
description:
'The id of the current view where the field is being created. Used to prevent auto-hiding the new field in this view.',
}),
order: z
.object({
viewId: z.string().meta({
Expand Down
5 changes: 4 additions & 1 deletion packages/v2/core/src/commands/CreateFieldCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const createFieldInputSchema = z.object({
baseId: z.string(),
tableId: z.string(),
field: tableFieldInputSchema,
viewId: z.string().optional(),
order: z
.object({
viewId: z.string(),
Expand All @@ -29,6 +30,7 @@ export class CreateFieldCommand extends TableUpdateCommand {
readonly baseId: BaseId,
readonly tableId: TableId,
readonly field: z.output<typeof tableFieldInputSchema>,
readonly viewId?: string,
readonly order?: {
viewId: string;
orderIndex: number;
Expand Down Expand Up @@ -57,7 +59,8 @@ export class CreateFieldCommand extends TableUpdateCommand {

return BaseId.create(parsed.data.baseId).andThen((baseId) =>
TableId.create(parsed.data.tableId).map(
(tableId) => new CreateFieldCommand(baseId, tableId, parsed.data.field, parsed.data.order)
(tableId) =>
new CreateFieldCommand(baseId, tableId, parsed.data.field, parsed.data.viewId, parsed.data.order)
)
);
}
Expand Down
8 changes: 8 additions & 0 deletions packages/v2/core/src/commands/CreateFieldHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ export class CreateFieldHandler implements ICommandHandler<CreateFieldCommand, C
domainContext,
};
if (!command.order) {
const currentViewId = command.viewId;
if (currentViewId) {
return ViewId.create(currentViewId).andThen((viewId) =>
table.update((mutator) =>
mutator.addField(field, { ...addFieldOptions, targetViewId: viewId })
)
);
}
return table.update((mutator) => mutator.addField(field, addFieldOptions));
}

Expand Down
83 changes: 83 additions & 0 deletions packages/v2/core/src/domain/table/Table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,89 @@ describe('Table', () => {
expect(targetEntry?.order).toBe(1.5);
});

it('keeps newly added field visible in the target view without explicit ordering', () => {
const newFieldId = createFieldId('k')._unsafeUnwrap();
const builder = Table.builder()
.withBaseId(createBaseId('k')._unsafeUnwrap())
.withName(TableName.create('Add Without Order')._unsafeUnwrap());

builder.field().singleLineText().withName(FieldName.create('Title')._unsafeUnwrap()).done();
builder.field().singleLineText().withName(FieldName.create('Notes')._unsafeUnwrap()).done();
builder.view().grid().withName(ViewName.create('View A')._unsafeUnwrap()).done();
builder.view().grid().withName(ViewName.create('View B')._unsafeUnwrap()).done();

const table = builder.build()._unsafeUnwrap();
const notesField = table.getFields().find((field) => field.name().toString() === 'Notes');
const targetView = table.views()[0];
const otherView = table.views()[1];

expect(notesField).toBeTruthy();
expect(targetView).toBeTruthy();
expect(otherView).toBeTruthy();
if (!notesField || !targetView || !otherView) return;

// Configure both views with explicit hidden visibility config
const targetViewMeta = targetView.columnMeta()._unsafeUnwrap().toDto();
const otherViewMeta = otherView.columnMeta()._unsafeUnwrap().toDto();

const configuredTargetMeta = ViewColumnMeta.create({
...targetViewMeta,
[notesField.id().toString()]: {
...(targetViewMeta[notesField.id().toString()] ?? {}),
hidden: true,
},
})._unsafeUnwrap();

const configuredOtherMeta = ViewColumnMeta.create({
...otherViewMeta,
[notesField.id().toString()]: {
...(otherViewMeta[notesField.id().toString()] ?? {}),
hidden: false,
},
})._unsafeUnwrap();

const configuredTable = TableUpdateViewColumnMetaSpec.create([
{
viewId: targetView.id(),
fieldId: notesField.id(),
columnMeta: configuredTargetMeta,
},
{
viewId: otherView.id(),
fieldId: notesField.id(),
columnMeta: configuredOtherMeta,
},
])
.mutate(table)
._unsafeUnwrap();

const newField = SingleLineTextField.create({
id: newFieldId,
name: FieldName.create('Added')._unsafeUnwrap(),
})._unsafeUnwrap();

// Add field with targetViewId but without viewOrder (simulates Add operator)
const updatedTable = configuredTable
.update((mutator) =>
mutator.addField(newField, {
targetViewId: targetView.id(),
})
)
._unsafeUnwrap().table;

const targetEntry = updatedTable.views()[0]?.columnMeta()._unsafeUnwrap().toDto()[
newField.id().toString()
];
const otherEntry = updatedTable.views()[1]?.columnMeta()._unsafeUnwrap().toDto()[
newField.id().toString()
];

// The new field should NOT be hidden in the target view (current view)
expect(targetEntry?.hidden).toBeUndefined();
// The new field SHOULD be hidden in the other view that has explicit hidden config
expect(otherEntry?.hidden).toBe(true);
});

it('rejects adding a field with duplicate dbFieldName', () => {
const baseIdResult = createBaseId('d');
const tableNameResult = TableName.create('Duplicate DbFieldName');
Expand Down
4 changes: 3 additions & 1 deletion packages/v2/core/src/domain/table/TableMutator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class TableMutateSpecBuilder extends SpecBuilder<Table, ITableSpecVisitor, Table
options?: {
foreignTables?: ReadonlyArray<Table>;
domainContext?: IDomainContext;
targetViewId?: ViewId;
viewOrder?: {
viewId: ViewId;
order: number;
Expand All @@ -62,7 +63,7 @@ class TableMutateSpecBuilder extends SpecBuilder<Table, ITableSpecVisitor, Table
const nextTableResult = this.currentTable.addField(field, {
foreignTables: options?.foreignTables,
domainContext: options?.domainContext,
targetViewId: options?.viewOrder?.viewId,
targetViewId: options?.viewOrder?.viewId ?? options?.targetViewId,
});
if (nextTableResult.isErr()) {
this.recordError(nextTableResult.error);
Expand Down Expand Up @@ -418,6 +419,7 @@ export class TableMutator {
field: Field,
options?: {
foreignTables?: ReadonlyArray<Table>;
targetViewId?: ViewId;
viewOrder?: {
viewId: ViewId;
order: number;
Expand Down
Loading