Browse Source

Merge pull request #8399 from nocodb/nc-one-to-one

Nc one to one
pull/8402/head
Pranav C 7 months ago committed by GitHub
parent
commit
d54b4e0381
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
  2. 21
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  3. 4
      packages/nc-gui/components/virtual-cell/OneToOne.vue
  4. 2
      packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue
  5. 1
      packages/nc-gui/lang/en.json
  6. 9
      packages/nocodb/src/db/BaseModelSqlv2.ts
  7. 9
      packages/nocodb/src/services/columns.service.ts
  8. 2
      tests/playwright/pages/Dashboard/Details/FieldsPage.ts
  9. 2
      tests/playwright/pages/Dashboard/Grid/Column/index.ts

6
packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue

@ -53,8 +53,6 @@ const refTables = computed(() => {
const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase()) const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase())
const isLinks = computed(() => vModel.value.uidt === UITypes.Links && vModel.value.type !== RelationTypes.ONE_TO_ONE) const isLinks = computed(() => vModel.value.uidt === UITypes.Links && vModel.value.type !== RelationTypes.ONE_TO_ONE)
const oneToOneEnabled = ref(false)
</script> </script>
<template> <template>
@ -62,9 +60,9 @@ const oneToOneEnabled = ref(false)
<div class="border-2 p-6"> <div class="border-2 p-6">
<a-form-item v-bind="validateInfos.type" class="nc-ltar-relation-type"> <a-form-item v-bind="validateInfos.type" class="nc-ltar-relation-type">
<a-radio-group v-model:value="vModel.type" name="type" v-bind="validateInfos.type" class="!flex flex-col gap-2"> <a-radio-group v-model:value="vModel.type" name="type" v-bind="validateInfos.type" class="!flex flex-col gap-2">
<a-radio value="hm" @dblclick="oneToOneEnabled = !oneToOneEnabled">{{ $t('title.hasMany') }}</a-radio> <a-radio value="oo">{{ $t('title.oneToOne') }}</a-radio>
<a-radio value="hm">{{ $t('title.hasMany') }}</a-radio>
<a-radio value="mm">{{ $t('title.manyToMany') }}</a-radio> <a-radio value="mm">{{ $t('title.manyToMany') }}</a-radio>
<a-radio v-if="oneToOneEnabled" value="oo">{{ $t('title.oneToOne') }}</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>

21
packages/nc-gui/components/smartsheet/header/VirtualCell.vue

@ -1,5 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnReqType, ColumnType, FormulaType, LinkToAnotherRecordType, LookupType, RollupType } from 'nocodb-sdk' import {
type ColumnReqType,
type ColumnType,
type FormulaType,
type LinkToAnotherRecordType,
type LookupType,
type RollupType,
isLinksOrLTAR,
} from 'nocodb-sdk'
import { RelationTypes, UITypes, UITypesName, substituteColumnIdWithAliasInFormula } from 'nocodb-sdk' import { RelationTypes, UITypes, UITypesName, substituteColumnIdWithAliasInFormula } from 'nocodb-sdk'
import { import {
ColumnInj, ColumnInj,
@ -12,6 +20,7 @@ import {
isHm, isHm,
isLookup, isLookup,
isMm, isMm,
isOo,
isRollup, isRollup,
isVirtualColRequired, isVirtualColRequired,
provide, provide,
@ -57,7 +66,7 @@ const colOptions = computed(() => column.value?.colOptions)
const tableTile = computed(() => meta?.value?.title) const tableTile = computed(() => meta?.value?.title)
const relationColumnOptions = computed<LinkToAnotherRecordType | null>(() => { const relationColumnOptions = computed<LinkToAnotherRecordType | null>(() => {
if (isMm(column.value) || isHm(column.value) || isBt(column.value)) { if (isLinksOrLTAR(column.value)) {
return column.value?.colOptions as LinkToAnotherRecordType return column.value?.colOptions as LinkToAnotherRecordType
} else if ((column?.value?.colOptions as LookupType | RollupType)?.fk_relation_column_id) { } else if ((column?.value?.colOptions as LookupType | RollupType)?.fk_relation_column_id) {
return meta?.value?.columns?.find( return meta?.value?.columns?.find(
@ -101,6 +110,8 @@ const tooltipMsg = computed(() => {
return `'${tableTile.value}' & '${relatedTableTitle.value}' ${t('labels.manyToMany')}` return `'${tableTile.value}' & '${relatedTableTitle.value}' ${t('labels.manyToMany')}`
} else if (isBt(column.value)) { } else if (isBt(column.value)) {
return `'${column?.value?.title}' ${t('labels.belongsTo')} '${relatedTableTitle.value}'` return `'${column?.value?.title}' ${t('labels.belongsTo')} '${relatedTableTitle.value}'`
} else if (isOo(column.value)) {
return `'${tableTile.value}' & '${relatedTableTitle.value}' ${t('labels.oneToOne')}`
} else if (isLookup(column.value)) { } else if (isLookup(column.value)) {
return `'${childColumn.value.title}' from '${relatedTableTitle.value}' (${childColumn.value.uidt})` return `'${childColumn.value.title}' from '${relatedTableTitle.value}' (${childColumn.value.uidt})`
} else if (isFormula(column.value)) { } else if (isFormula(column.value)) {
@ -116,6 +127,10 @@ const tooltipMsg = computed(() => {
return column?.value?.title || '' return column?.value?.title || ''
}) })
const showTooltipAlways = computed(() => {
return isLinksOrLTAR(column.value) || isFormula(column.value) || isRollup(column.value) || isLookup(column.value)
})
const columnOrder = ref<Pick<ColumnReqType, 'column_order'> | null>(null) const columnOrder = ref<Pick<ColumnReqType, 'column_order'> | null>(null)
const columnTypeName = computed(() => { const columnTypeName = computed(() => {
@ -172,7 +187,7 @@ const openDropDown = (e: Event) => {
</NcTooltip> </NcTooltip>
<LazySmartsheetHeaderVirtualCellIcon v-else /> <LazySmartsheetHeaderVirtualCellIcon v-else />
</template> </template>
<NcTooltip placement="bottom" class="truncate name pl-1" show-on-truncate-only> <NcTooltip placement="bottom" class="truncate name pl-1" :show-on-truncate-only="!showTooltipAlways">
<template #title> <template #title>
{{ tooltipMsg }} {{ tooltipMsg }}
</template> </template>

4
packages/nc-gui/components/virtual-cell/OneToOne.vue

@ -55,7 +55,9 @@ const value = computed(() => {
if (cellValue?.value) { if (cellValue?.value) {
return cellValue?.value return cellValue?.value
} else if (isNew.value) { } else if (isNew.value) {
return state?.value?.[column?.value.title as string] const columnTitle = column?.value.title as string
const columnValue = state?.value?.[columnTitle]
return Array.isArray(columnValue) ? columnValue[0] : columnValue
} }
return null return null
}) })

2
packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue

@ -142,7 +142,7 @@ const newRowState = computed(() => {
const relatedTableColOpt = colInRelatedTable?.colOptions as LinkToAnotherRecordType const relatedTableColOpt = colInRelatedTable?.colOptions as LinkToAnotherRecordType
if (!relatedTableColOpt) return {} if (!relatedTableColOpt) return {}
if (relatedTableColOpt.type === RelationTypes.BELONGS_TO) { if (relatedTableColOpt.type === RelationTypes.BELONGS_TO || relatedTableColOpt.type === RelationTypes.ONE_TO_ONE) {
return { return {
[colInRelatedTable.title as string]: row?.value?.row, [colInRelatedTable.title as string]: row?.value?.row,
} }

1
packages/nc-gui/lang/en.json

@ -714,6 +714,7 @@
"hasMany": "has many", "hasMany": "has many",
"belongsTo": "belongs to", "belongsTo": "belongs to",
"manyToMany": "have many to many relation", "manyToMany": "have many to many relation",
"oneToOne": "have one to one relation",
"extraConnectionParameters": "Extra connection parameters", "extraConnectionParameters": "Extra connection parameters",
"commentsOnly": "Comments only", "commentsOnly": "Comments only",
"documentation": "Documentation", "documentation": "Documentation",

9
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -3314,16 +3314,17 @@ class BaseModelSqlv2 {
await childModel.getColumns(); await childModel.getColumns();
if (isBt) { if (isBt) {
// if array then extract value from first element
const colVal = Array.isArray(nestedData)
? nestedData[0]?.[childModel.primaryKey.title]
: nestedData[childModel.primaryKey.title];
// todo: unlink the ref record // todo: unlink the ref record
preInsertOps.push(async () => { preInsertOps.push(async () => {
return this.dbDriver(this.getTnPath(childModel.table_name)) return this.dbDriver(this.getTnPath(childModel.table_name))
.update({ .update({
[childCol.column_name]: null, [childCol.column_name]: null,
}) })
.where( .where(childCol.column_name, colVal)
childCol.column_name,
nestedData[childModel.primaryKey.title],
)
.toQuery(); .toQuery();
}); });

9
packages/nocodb/src/services/columns.service.ts

@ -5,6 +5,7 @@ import {
isCreatedOrLastModifiedTimeCol, isCreatedOrLastModifiedTimeCol,
isLinksOrLTAR, isLinksOrLTAR,
isVirtualCol, isVirtualCol,
RelationTypes,
substituteColumnAliasWithIdInFormula, substituteColumnAliasWithIdInFormula,
substituteColumnIdWithAliasInFormula, substituteColumnIdWithAliasInFormula,
UITypes, UITypes,
@ -18,7 +19,6 @@ import type {
ColumnReqType, ColumnReqType,
LinkToAnotherColumnReqType, LinkToAnotherColumnReqType,
LinkToAnotherRecordType, LinkToAnotherRecordType,
RelationTypes,
UserType, UserType,
} from 'nocodb-sdk'; } from 'nocodb-sdk';
import type CustomKnex from '~/db/CustomKnex'; import type CustomKnex from '~/db/CustomKnex';
@ -2417,9 +2417,9 @@ export class ColumnsService {
}; };
await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody); await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody);
}
// delete foreign key column // delete foreign key column
await Column.delete(childColumn.id, ncMeta); await Column.delete(childColumn.id, ncMeta);
}
}; };
deleteOoRelation = async ( deleteOoRelation = async (
@ -2492,7 +2492,7 @@ export class ColumnsService {
const columnsInRelatedTable: Column[] = await relationColOpt const columnsInRelatedTable: Column[] = await relationColOpt
.getRelatedTable(ncMeta) .getRelatedTable(ncMeta)
.then((m) => m.getColumns(ncMeta)); .then((m) => m.getColumns(ncMeta));
const relType = relationColOpt.type === 'bt' ? 'hm' : 'bt'; const relType = RelationTypes.ONE_TO_ONE;
for (const c of columnsInRelatedTable) { for (const c of columnsInRelatedTable) {
if (c.uidt !== UITypes.LinkToAnotherRecord) continue; if (c.uidt !== UITypes.LinkToAnotherRecord) continue;
const colOpt = await c.getColOptions<LinkToAnotherRecordColumn>(ncMeta); const colOpt = await c.getColOptions<LinkToAnotherRecordColumn>(ncMeta);
@ -2562,9 +2562,10 @@ export class ColumnsService {
}; };
await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody); await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody);
}
// delete foreign key column // delete foreign key column
await Column.delete(childColumn.id, ncMeta); await Column.delete(childColumn.id, ncMeta);
}
}; };
async createLTARColumn(param: { async createLTARColumn(param: {

2
tests/playwright/pages/Dashboard/Details/FieldsPage.ts

@ -186,7 +186,7 @@ export class FieldsPage extends BasePage {
case 'Links': case 'Links':
await this.addOrEditColumn await this.addOrEditColumn
.locator('.nc-ltar-relation-type >> .ant-radio') .locator('.nc-ltar-relation-type >> .ant-radio')
.nth(relationType === 'Has Many' ? 0 : 1) .nth(relationType === 'Has Many' ? 1 : 2)
.click(); .click();
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); await this.addOrEditColumn.locator('.ant-select-single').nth(1).click();
await this.rootPage.locator(`.nc-ltar-child-table >> input[type="search"]`).fill(childTable); await this.rootPage.locator(`.nc-ltar-child-table >> input[type="search"]`).fill(childTable);

2
tests/playwright/pages/Dashboard/Grid/Column/index.ts

@ -173,7 +173,7 @@ export class ColumnPageObject extends BasePage {
case 'Links': case 'Links':
await this.get() await this.get()
.locator('.nc-ltar-relation-type >> .ant-radio') .locator('.nc-ltar-relation-type >> .ant-radio')
.nth(relationType === 'Has Many' ? 0 : 1) .nth(relationType === 'Has Many' ? 1 : 2)
.click(); .click();
await this.get().locator('.ant-select-single').nth(1).click(); await this.get().locator('.ant-select-single').nth(1).click();
await this.rootPage.locator(`.nc-ltar-child-table >> input[type="search"]`).fill(childTable); await this.rootPage.locator(`.nc-ltar-child-table >> input[type="search"]`).fill(childTable);

Loading…
Cancel
Save