Browse Source

Merge pull request #9827 from nocodb/nc-fix/misc

Nc fix/miscellaneous bug fixes
pull/9724/merge
Pranav C 1 week ago committed by GitHub
parent
commit
7e37415092
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      packages/nc-gui/components/general/Gift.vue
  2. 6
      packages/nc-gui/components/smartsheet/Form.vue
  3. 3
      packages/nc-gui/components/smartsheet/column/FormulaOptions.vue
  4. 6
      packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
  5. 2
      packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue
  6. 8
      packages/nc-gui/composables/useApi/interceptors.ts
  7. 6
      packages/nc-gui/composables/useGlobal/types.ts
  8. 8
      packages/nc-gui/utils/dataUtils.ts
  9. 4
      packages/nocodb-sdk/src/lib/Api.ts
  10. 4
      packages/nocodb/src/db/BaseModelSqlv2.ts
  11. 2
      packages/nocodb/src/helpers/populateSamplePayload.ts
  12. 284
      packages/nocodb/src/models/Column.ts
  13. 2
      packages/nocodb/src/services/hooks.service.ts

2
packages/nc-gui/components/general/Gift.vue

@ -59,7 +59,7 @@ const closeAndShowAgain = () => {
<GeneralIcon class="icon" icon="gift" size="xlarge" /> <GeneralIcon class="icon" icon="gift" size="xlarge" />
<h4>Gifts Unlocked!</h4> <h4>Gifts Unlocked!</h4>
</div> </div>
<div class="body">We are giving away $100 worth of amazon coupons to our pro open source users!</div> <div class="body">We are giving away $50 worth of amazon coupons to our pro open source users!</div>
</div> </div>
<div v-if="!hideImage && !giftBannerDismissedCount" class="img-wrapper"> <div v-if="!hideImage && !giftBannerDismissedCount" class="img-wrapper">
<img src="~assets/img/giftCard.svg" /> <img src="~assets/img/giftCard.svg" />

6
packages/nc-gui/components/smartsheet/Form.vue

@ -339,13 +339,15 @@ async function submitForm() {
} }
} }
await insertRow({ const res = await insertRow({
row: { ...formState.value, ...state.value }, row: { ...formState.value, ...state.value },
oldRow: {}, oldRow: {},
rowMeta: { new: true }, rowMeta: { new: true },
}) })
submitted.value = true if (res) {
submitted.value = true
}
} }
async function clearForm() { async function clearForm() {

3
packages/nc-gui/components/smartsheet/column/FormulaOptions.vue

@ -127,6 +127,9 @@ watch(
() => { () => {
debouncedValidate() debouncedValidate()
}, },
{
immediate: true,
},
) )
// set additional validations // set additional validations

6
packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue

@ -2136,7 +2136,8 @@ watch(
'active-cell !after:h-[calc(100%-2px)]': 'active-cell !after:h-[calc(100%-2px)]':
(activeCell.row === row.rowMeta.rowIndex && activeCell.col === 0) || (activeCell.row === row.rowMeta.rowIndex && activeCell.col === 0) ||
(selectedRange._start?.row === row.rowMeta.rowIndex && selectedRange._start?.col === 0), (selectedRange._start?.row === row.rowMeta.rowIndex && selectedRange._start?.col === 0),
'nc-required-cell': cellMeta[index]?.[0]?.isColumnRequiredAndNull && !isPublicView, 'nc-required-cell':
!row.rowMeta?.isLoading && cellMeta[index]?.[0]?.isColumnRequiredAndNull && !isPublicView,
'align-middle': !rowHeightEnum || rowHeightEnum === 1, 'align-middle': !rowHeightEnum || rowHeightEnum === 1,
'align-top': rowHeightEnum && rowHeightEnum !== 1, 'align-top': rowHeightEnum && rowHeightEnum !== 1,
'filling': fillRangeMap[`${row.rowMeta.rowIndex}-0`], 'filling': fillRangeMap[`${row.rowMeta.rowIndex}-0`],
@ -2207,7 +2208,8 @@ watch(
'active-cell': 'active-cell':
(activeCell.row === row.rowMeta.rowIndex && activeCell.col === colIndex) || (activeCell.row === row.rowMeta.rowIndex && activeCell.col === colIndex) ||
(selectedRange._start?.row === row.rowMeta.rowIndex && selectedRange._start?.col === colIndex), (selectedRange._start?.row === row.rowMeta.rowIndex && selectedRange._start?.col === colIndex),
'nc-required-cell': cellMeta[index][colIndex].isColumnRequiredAndNull && !isPublicView, 'nc-required-cell':
!row.rowMeta?.isLoading && cellMeta[index][colIndex].isColumnRequiredAndNull && !isPublicView,
'align-middle': !rowHeightEnum || rowHeightEnum === 1, 'align-middle': !rowHeightEnum || rowHeightEnum === 1,
'align-top': rowHeightEnum && rowHeightEnum !== 1, 'align-top': rowHeightEnum && rowHeightEnum !== 1,
'filling': fillRangeMap[`${row.rowMeta.rowIndex}-${colIndex}`], 'filling': fillRangeMap[`${row.rowMeta.rowIndex}-${colIndex}`],

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

@ -66,7 +66,7 @@ const relation = computed(() => {
const linkRow = async (row: Record<string, any>, id: number) => { const linkRow = async (row: Record<string, any>, id: number) => {
if (isNew.value) { if (isNew.value) {
addLTARRef(row, injectedColumn?.value as ColumnType) await addLTARRef(row, injectedColumn?.value as ColumnType)
if (relation.value === 'oo' || relation.value === 'bt') { if (relation.value === 'oo' || relation.value === 'bt') {
isChildrenExcludedListLinked.value.forEach((isLinked, idx) => { isChildrenExcludedListLinked.value.forEach((isLinked, idx) => {
if (isLinked) { if (isLinked) {

8
packages/nc-gui/composables/useApi/interceptors.ts

@ -67,6 +67,14 @@ export function addAxiosInterceptors(api: Api<any>) {
skipLogout: true, skipLogout: true,
}) })
if (!token) {
await state.signOut({
redirectToSignin: !isSharedPage,
skipApiCall: true,
})
return Promise.reject(error)
}
const config = error.config const config = error.config
config.headers['xc-auth'] = token config.headers['xc-auth'] = token

6
packages/nc-gui/composables/useGlobal/types.ts

@ -92,7 +92,11 @@ export interface SignOutParams {
export interface Actions { export interface Actions {
signOut: (signOutParams?: SignOutParams) => Promise<void> signOut: (signOutParams?: SignOutParams) => Promise<void>
signIn: (token: string, keepProps?: boolean) => void signIn: (token: string, keepProps?: boolean) => void
refreshToken: (params: { axiosInstance?: AxiosInstance; skipLogout?: boolean; cognitoOnly?: boolean }) => Promise<void> refreshToken: (params: {
axiosInstance?: AxiosInstance
skipLogout?: boolean
cognitoOnly?: boolean
}) => Promise<string | null | void>
loadAppInfo: () => void loadAppInfo: () => void
setIsMobileMode: (isMobileMode: boolean) => void setIsMobileMode: (isMobileMode: boolean) => void
navigateToProject: (params: { workspaceId?: string; baseId?: string; type?: NcProjectType; query?: any }) => void navigateToProject: (params: { workspaceId?: string; baseId?: string; type?: NcProjectType; query?: any }) => void

8
packages/nc-gui/utils/dataUtils.ts

@ -91,14 +91,14 @@ export async function populateInsertObject({
col.uidt === UITypes.LinkToAnotherRecord && col.uidt === UITypes.LinkToAnotherRecord &&
(<LinkToAnotherRecordType>col.colOptions).type === RelationTypes.BELONGS_TO (<LinkToAnotherRecordType>col.colOptions).type === RelationTypes.BELONGS_TO
) { ) {
if (ltarState[col.title!]) { if (ltarState[col.title!] || row[col.title!]) {
const ltarVal = ltarState[col.title!] || row[col.title!]
const colOpt = <LinkToAnotherRecordType>col.colOptions const colOpt = <LinkToAnotherRecordType>col.colOptions
const childCol = meta.columns!.find((c) => colOpt.fk_child_column_id === c.id) const childCol = meta.columns!.find((c) => colOpt.fk_child_column_id === c.id)
const relatedTableMeta = (await getMeta(colOpt.fk_related_model_id!)) as TableType const relatedTableMeta = (await getMeta(colOpt.fk_related_model_id!)) as TableType
if (relatedTableMeta && childCol) { if (relatedTableMeta && childCol) {
o[childCol.title!] = o[childCol.title!] = ltarVal[relatedTableMeta!.columns!.find((c) => c.id === colOpt.fk_parent_column_id)!.title!]
ltarState[col.title!][relatedTableMeta!.columns!.find((c) => c.id === colOpt.fk_parent_column_id)!.title!] if (o[childCol.title!] !== null && o[childCol.title!] !== undefined) missingRequiredColumns.delete(childCol.title)
missingRequiredColumns.delete(childCol.title)
} }
} }
} }

4
packages/nocodb-sdk/src/lib/Api.ts

@ -2846,6 +2846,8 @@ export interface ViewType {
| MapType | MapType
| CalendarType | CalendarType
| (FormType & GalleryType & GridType & KanbanType & MapType & CalendarType); | (FormType & GalleryType & GridType & KanbanType & MapType & CalendarType);
/** ID of view owner user */
owned_by?: IdType;
} }
/** /**
@ -2918,6 +2920,8 @@ export interface ViewUpdateReqType {
order?: number; order?: number;
/** Should this view show system fields? */ /** Should this view show system fields? */
show_system_fields?: BoolType; show_system_fields?: BoolType;
/** ID of view owner user */
owned_by?: IdType;
} }
/** /**

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

@ -4973,7 +4973,7 @@ class BaseModelSqlv2 {
let response; let response;
const query = this.dbDriver(this.tnPath).insert(insertObj); const query = this.dbDriver(this.tnPath).insert(insertObj);
if (this.isPg || this.isMssql) { if ((this.isPg || this.isMssql) && this.model.primaryKey) {
query.returning( query.returning(
`${this.model.primaryKey.column_name} as ${this.model.primaryKey.id}`, `${this.model.primaryKey.column_name} as ${this.model.primaryKey.id}`,
); );
@ -5053,7 +5053,7 @@ class BaseModelSqlv2 {
await this.runOps(postInsertOps.map((f) => f(rowId))); await this.runOps(postInsertOps.map((f) => f(rowId)));
if (rowId !== null && rowId !== undefined) { if (this.model.primaryKey && rowId !== null && rowId !== undefined) {
response = await this.readRecord({ response = await this.readRecord({
idOrRecord: rowId, idOrRecord: rowId,
validateFormula: false, validateFormula: false,

2
packages/nocodb/src/helpers/populateSamplePayload.ts

@ -77,8 +77,6 @@ export async function populateSamplePayloadV2(
data: { data: {
table_id: model.id, table_id: model.id,
table_name: model.title, table_name: model.title,
view_id: model.views[0].id,
view_name: model.views[0].title,
}, },
}; };

284
packages/nocodb/src/models/Column.ts

@ -57,6 +57,27 @@ const selectColors = [
const logger = new Logger('Column'); const logger = new Logger('Column');
const requiredColumnsToRecreate = {
[UITypes.LinkToAnotherRecord]: [
'type',
'fk_child_column_id',
'fk_parent_column_id',
'fk_related_model_id',
],
[UITypes.Links]: [
'type',
'fk_child_column_id',
'fk_parent_column_id',
'fk_related_model_id',
],
[UITypes.Rollup]: ['fk_relation_column_id', 'fk_rollup_column_id'],
[UITypes.Lookup]: ['fk_relation_column_id', 'fk_lookup_column_id'],
[UITypes.QrCode]: ['fk_qr_value_column_id'],
[UITypes.Barcode]: ['fk_barcode_value_column_id'],
[UITypes.Button]: ['type', 'label'],
[UITypes.Formula]: ['formula'],
};
export default class Column<T = any> implements ColumnType { export default class Column<T = any> implements ColumnType {
public fk_model_id: string; public fk_model_id: string;
public fk_workspace_id?: string; public fk_workspace_id?: string;
@ -1112,157 +1133,148 @@ export default class Column<T = any> implements ColumnType {
skipFormulaInvalidate = false, skipFormulaInvalidate = false,
) { ) {
const oldCol = await Column.get(context, { colId }, ncMeta); const oldCol = await Column.get(context, { colId }, ncMeta);
let insertColOpt = true; const requiredColAvail =
switch (oldCol.uidt) { !requiredColumnsToRecreate[oldCol.uidt] ||
case UITypes.Lookup: { requiredColumnsToRecreate[oldCol.uidt].every((k) => column[k]);
// LookupColumn.insert()
await ncMeta.metaDelete( if (requiredColAvail) {
context.workspace_id, switch (oldCol.uidt) {
context.base_id, case UITypes.Lookup: {
MetaTable.COL_LOOKUP, // LookupColumn.insert()
{
fk_column_id: colId,
},
);
await NocoCache.deepDel(
`${CacheScope.COL_LOOKUP}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT,
);
break;
}
case UITypes.Rollup: {
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.COL_ROLLUP,
{
fk_column_id: colId,
},
);
await NocoCache.deepDel(
`${CacheScope.COL_ROLLUP}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT,
);
break;
}
case UITypes.Links: await ncMeta.metaDelete(
case UITypes.LinkToAnotherRecord: { context.workspace_id,
// delete only if all required fields are present context.base_id,
if ( MetaTable.COL_LOOKUP,
[ {
'type', fk_column_id: colId,
'fk_child_column_id', },
'fk_parent_column_id', );
'fk_related_model_id', await NocoCache.deepDel(
].some((k) => !column[k]) `${CacheScope.COL_LOOKUP}:${colId}`,
) { CacheDelDirection.CHILD_TO_PARENT,
insertColOpt = false; );
break;
}
case UITypes.Rollup: {
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.COL_ROLLUP,
{
fk_column_id: colId,
},
);
await NocoCache.deepDel(
`${CacheScope.COL_ROLLUP}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT,
);
break; break;
} }
await ncMeta.metaDelete( case UITypes.Links:
context.workspace_id, case UITypes.LinkToAnotherRecord: {
context.base_id, await ncMeta.metaDelete(
MetaTable.COL_RELATIONS, context.workspace_id,
{ context.base_id,
fk_column_id: colId, MetaTable.COL_RELATIONS,
}, {
); fk_column_id: colId,
await NocoCache.deepDel( },
`${CacheScope.COL_RELATION}:${colId}`, );
CacheDelDirection.CHILD_TO_PARENT, await NocoCache.deepDel(
); `${CacheScope.COL_RELATION}:${colId}`,
break; CacheDelDirection.CHILD_TO_PARENT,
} );
case UITypes.Formula: { break;
await ncMeta.metaDelete( }
context.workspace_id, case UITypes.Formula: {
context.base_id, await ncMeta.metaDelete(
MetaTable.COL_FORMULA, context.workspace_id,
{ context.base_id,
fk_column_id: colId, MetaTable.COL_FORMULA,
}, {
); fk_column_id: colId,
},
);
await NocoCache.deepDel( await NocoCache.deepDel(
`${CacheScope.COL_FORMULA}:${colId}`, `${CacheScope.COL_FORMULA}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT, CacheDelDirection.CHILD_TO_PARENT,
); );
break; break;
} }
case UITypes.Button: { case UITypes.Button: {
await ncMeta.metaDelete( await ncMeta.metaDelete(
context.workspace_id, context.workspace_id,
context.base_id, context.base_id,
MetaTable.COL_BUTTON, MetaTable.COL_BUTTON,
{ {
fk_column_id: colId, fk_column_id: colId,
}, },
); );
await NocoCache.deepDel( await NocoCache.deepDel(
`${CacheScope.COL_BUTTON}:${colId}`, `${CacheScope.COL_BUTTON}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT, CacheDelDirection.CHILD_TO_PARENT,
); );
break; break;
} }
case UITypes.QrCode: { case UITypes.QrCode: {
await ncMeta.metaDelete( await ncMeta.metaDelete(
context.workspace_id, context.workspace_id,
context.base_id, context.base_id,
MetaTable.COL_QRCODE, MetaTable.COL_QRCODE,
{ {
fk_column_id: colId, fk_column_id: colId,
}, },
); );
await NocoCache.deepDel( await NocoCache.deepDel(
`${CacheScope.COL_QRCODE}:${colId}`, `${CacheScope.COL_QRCODE}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT, CacheDelDirection.CHILD_TO_PARENT,
); );
break; break;
} }
case UITypes.Barcode: { case UITypes.Barcode: {
await ncMeta.metaDelete( await ncMeta.metaDelete(
context.workspace_id, context.workspace_id,
context.base_id, context.base_id,
MetaTable.COL_BARCODE, MetaTable.COL_BARCODE,
{ {
fk_column_id: colId, fk_column_id: colId,
}, },
); );
await NocoCache.deepDel( await NocoCache.deepDel(
`${CacheScope.COL_BARCODE}:${colId}`, `${CacheScope.COL_BARCODE}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT, CacheDelDirection.CHILD_TO_PARENT,
); );
break; break;
} }
case UITypes.MultiSelect: case UITypes.MultiSelect:
case UITypes.SingleSelect: { case UITypes.SingleSelect: {
await ncMeta.metaDelete( await ncMeta.metaDelete(
context.workspace_id, context.workspace_id,
context.base_id, context.base_id,
MetaTable.COL_SELECT_OPTIONS, MetaTable.COL_SELECT_OPTIONS,
{ {
fk_column_id: colId, fk_column_id: colId,
}, },
); );
await NocoCache.deepDel( await NocoCache.deepDel(
`${CacheScope.COL_SELECT_OPTION}:${colId}:list`, `${CacheScope.COL_SELECT_OPTION}:${colId}:list`,
CacheDelDirection.PARENT_TO_CHILD, CacheDelDirection.PARENT_TO_CHILD,
); );
break; break;
}
} }
} }
const updateObj = extractProps(column, [ const updateObj = extractProps(column, [
'column_name', 'column_name',
'title', 'title',
@ -1362,7 +1374,7 @@ export default class Column<T = any> implements ColumnType {
); );
// insert new col options only if existing colOption meta is deleted // insert new col options only if existing colOption meta is deleted
if (insertColOpt) if (requiredColAvail)
await this.insertColOption(context, column, colId, ncMeta); await this.insertColOption(context, column, colId, ncMeta);
// on column update, delete any optimised single query cache // on column update, delete any optimised single query cache

2
packages/nocodb/src/services/hooks.service.ts

@ -225,7 +225,7 @@ export class HooksService {
model: model, model: model,
view: null, view: null,
prevData: null, prevData: null,
newData: data, newData: data.rows,
user: user, user: user,
testFilters: (hook as any)?.filters, testFilters: (hook as any)?.filters,
throwErrorOnFailure: true, throwErrorOnFailure: true,

Loading…
Cancel
Save