diff --git a/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts b/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts index 085573a68..159469d7f 100644 --- a/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts +++ b/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts @@ -218,13 +218,25 @@ export class FieldConvertingService { private async generateReferenceFieldOps(fieldId: string) { const topoOrdersContext = await this.fieldCalculationService.getTopoOrdersContext([fieldId]); + const { fieldMap, fieldId2TableId, directedGraph } = topoOrdersContext; - const { fieldMap, fieldId2TableId } = topoOrdersContext; + // Find affected fields using directedGraph + const affectedFields = new Set(); - // get all fields after current field - const topoOrders = topoOrdersContext.topoOrders.slice( - topoOrdersContext.topoOrders.findIndex((item) => item.id === fieldId) + 1 - ); + function findAffectedFields(currentId: string) { + for (const { fromFieldId, toFieldId } of directedGraph) { + if (fromFieldId === currentId && !affectedFields.has(toFieldId)) { + affectedFields.add(toFieldId); + findAffectedFields(toFieldId); + } + } + } + + // Start from the initial field + findAffectedFields(fieldId); + + // Filter topoOrders to only include affected fields + const topoOrders = topoOrdersContext.topoOrders.filter((item) => affectedFields.has(item.id)); if (!topoOrders.length) { return {}; @@ -234,9 +246,9 @@ export class FieldConvertingService { for (let i = 0; i < topoOrders.length; i++) { const topoOrder = topoOrders[i]; - // curField will be mutate in loop const curField = fieldMap[topoOrder.id]; const tableId = fieldId2TableId[curField.id]; + if (curField.isLookup) { pushOpsMap(tableId, curField.id, this.updateLookupField(curField, fieldMap)); } else if (curField.type === FieldType.Formula) { diff --git a/apps/nestjs-backend/test/field-converting.e2e-spec.ts b/apps/nestjs-backend/test/field-converting.e2e-spec.ts index 8cc47817a..773108e16 100644 --- a/apps/nestjs-backend/test/field-converting.e2e-spec.ts +++ b/apps/nestjs-backend/test/field-converting.e2e-spec.ts @@ -520,6 +520,54 @@ describe('OpenAPI Freely perform column transformations (e2e)', () => { expect(newField.name).toEqual('my name'); expect(newField.description).toEqual('world'); }); + + // A -> B -> C + // D -> E -> C + // should not update E when A update + // all context: A, B, C, E + // update context: A, B, C + it('should not update E when A update', async () => { + const aField = await createField(table1.id, { + type: FieldType.Number, + }); + + const bField = await createField(table1.id, { + type: FieldType.Formula, + options: { + expression: `{${aField.id}}`, + }, + }); + + const dField = await createField(table1.id, { + type: FieldType.Number, + }); + + const eField = await createField(table1.id, { + type: FieldType.Formula, + options: { + expression: `{${dField.id}}`, + }, + }); + + const cField = await createField(table1.id, { + type: FieldType.Formula, + options: { + expression: `{${bField.id}} + {${eField.id}}`, + }, + }); + + await updateRecordByApi(table1.id, table1.records[0].id, aField.id, 1); + + await convertField(table1.id, bField.id, { + type: FieldType.Formula, + options: { + expression: `{${aField.id}} & ''`, + }, + }); + + const record1 = await getRecord(table1.id, table1.records[0].id); + expect(record1.fields[cField.id]).toEqual('1null'); + }); }); describe('convert text field', () => { diff --git a/apps/nextjs-app/package.json b/apps/nextjs-app/package.json index 5bcdb169c..73c2b1f2e 100644 --- a/apps/nextjs-app/package.json +++ b/apps/nextjs-app/package.json @@ -123,7 +123,7 @@ "@teable/common-i18n": "workspace:^", "@teable/core": "workspace:^", "@teable/icons": "workspace:^", - "@teable/next-themes": "0.3.3", + "@teable/next-themes": "0.3.5", "@teable/openapi": "workspace:^", "@teable/sdk": "workspace:^", "@teable/ui-lib": "workspace:^", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d18485c6d..c8fa7f806 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -72,7 +72,7 @@ "@teable/common-i18n": "workspace:*", "@teable/core": "workspace:*", "@teable/icons": "workspace:*", - "@teable/next-themes": "0.3.3", + "@teable/next-themes": "0.3.5", "@teable/openapi": "workspace:*", "@teable/ui-lib": "workspace:*", "@udecode/cn": "37.0.0", diff --git a/packages/ui-lib/package.json b/packages/ui-lib/package.json index bf02a43a0..7f77fa821 100644 --- a/packages/ui-lib/package.json +++ b/packages/ui-lib/package.json @@ -141,7 +141,7 @@ "@radix-ui/react-toggle-group": "1.1.0", "@radix-ui/react-tooltip": "1.0.7", "@teable/icons": "workspace:^", - "@teable/next-themes": "0.3.3", + "@teable/next-themes": "0.3.5", "class-variance-authority": "0.7.0", "clsx": "2.1.0", "cmdk": "1.0.0", diff --git a/plugins/package.json b/plugins/package.json index c8ea4e729..7d858c594 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -18,7 +18,7 @@ "@teable/common-i18n": "workspace:^", "@teable/core": "workspace:^", "@teable/icons": "workspace:^", - "@teable/next-themes": "0.3.3", + "@teable/next-themes": "0.3.5", "@teable/openapi": "workspace:^", "@teable/sdk": "workspace:^", "@teable/ui-lib": "workspace:^", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2dcecb610..9394d9516 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -602,8 +602,8 @@ importers: specifier: workspace:^ version: link:../../packages/icons '@teable/next-themes': - specifier: 0.3.3 - version: 0.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.3.5 + version: 0.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@teable/openapi': specifier: workspace:^ version: link:../../packages/openapi @@ -1400,8 +1400,8 @@ importers: specifier: workspace:* version: link:../icons '@teable/next-themes': - specifier: 0.3.3 - version: 0.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.3.5 + version: 0.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@teable/openapi': specifier: workspace:* version: link:../openapi @@ -1719,8 +1719,8 @@ importers: specifier: workspace:^ version: link:../icons '@teable/next-themes': - specifier: 0.3.3 - version: 0.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.3.5 + version: 0.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) class-variance-authority: specifier: 0.7.0 version: 0.7.0 @@ -1906,8 +1906,8 @@ importers: specifier: workspace:^ version: link:../packages/icons '@teable/next-themes': - specifier: 0.3.3 - version: 0.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.3.5 + version: 0.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@teable/openapi': specifier: workspace:^ version: link:../packages/openapi @@ -6892,8 +6892,8 @@ packages: '@tanstack/virtual-core@3.2.0': resolution: {integrity: sha512-P5XgYoAw/vfW65byBbJQCw+cagdXDT/qH6wmABiLt4v4YBT2q2vqCOhihe+D1Nt325F/S/0Tkv6C5z0Lv+VBQQ==} - '@teable/next-themes@0.3.3': - resolution: {integrity: sha512-qGQWYVymiLXlIS8N38LrfPulxxG1Us+Qq25p/N5FJBmt7NAOkohuY4BhXTLPw+ovdE8UI+OlNnqPwKbfL5Azbw==} + '@teable/next-themes@0.3.5': + resolution: {integrity: sha512-/P0GxY6Yqu8OGL3Kn3/WqCz/w+Ly5WKyol9hRDBnhIm2nq+yFIkI588tdcWHXatub0ASbrS/uDW7qgcDsWmNIw==} peerDependencies: react: ^16.8 || ^17 || ^18 react-dom: ^16.8 || ^17 || ^18 @@ -23428,7 +23428,7 @@ snapshots: '@tanstack/virtual-core@3.2.0': {} - '@teable/next-themes@0.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@teable/next-themes@0.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -27671,7 +27671,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.0) eslint-plugin-react: 7.37.1(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -27723,7 +27723,7 @@ snapshots: is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node @@ -27799,7 +27799,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8