Browse Source

Merge branch 'develop' into fix/formula

pull/1979/head
Wing-Kam Wong 3 years ago
parent
commit
c62acc06bc
  1. 11
      packages/nc-gui/components/project/spreadsheet/components/Cell.vue
  2. 38
      packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue
  3. 19
      packages/nc-gui/components/project/spreadsheet/components/EditableCell.vue
  4. 22
      packages/nc-gui/components/project/spreadsheet/components/cell/EmailCell.vue
  5. 14
      packages/nc-gui/components/project/spreadsheet/components/cell/UrlCell.vue
  6. 98
      packages/nc-gui/components/project/spreadsheet/components/editColumn/RatingOptions.vue
  7. 95
      packages/nc-gui/components/project/spreadsheet/components/editColumn/checkboxOptions.vue
  8. 36
      packages/nc-gui/components/project/spreadsheet/components/editableCell/BooleanCell.vue
  9. 9
      packages/nc-gui/components/project/spreadsheet/components/editableCell/EditableUrlCell.vue
  10. 71
      packages/nc-gui/components/project/spreadsheet/components/editableCell/RatingCell.vue
  11. 6
      packages/nc-gui/components/project/spreadsheet/mixins/cell.js
  12. 70
      packages/nc-gui/components/project/spreadsheet/views/GridView.vue
  13. 64
      packages/nc-gui/lang/zh_CN.json
  14. 24
      packages/noco-docs/content/en/setup-and-usages/import-airtable-to-sql-database-within-a-minute-for-free.md
  15. 84
      packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts
  16. 74
      packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts
  17. 36
      packages/nocodb-sdk/src/lib/sqlUi/OracleUi.ts
  18. 97
      packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts
  19. 94
      packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts
  20. 3
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSqlv2.ts
  21. 40
      packages/nocodb/src/lib/noco-models/Column.ts
  22. 6
      packages/nocodb/src/lib/noco/common/XcMigrationSourcev2.ts
  23. 13
      packages/nocodb/src/lib/noco/meta/api/sync/helpers/job.ts
  24. 38
      packages/nocodb/src/lib/noco/migrationsv2/nc_015_add_meta_col_in_column_table.ts
  25. 4
      packages/nocodb/src/lib/noco/upgrader/jobs/ncProjectUpgraderV2_0090000.ts
  26. 10
      scripts/markdown/readme/languages/chinese.md

11
packages/nc-gui/components/project/spreadsheet/components/Cell.vue

@ -11,11 +11,14 @@
<!-- <enum-list-editable-cell @click.stop="$emit('enableedit')" v-else-if="isEnum && selected" :value="value" :column="column"></enum-list-editable-cell>--> <!-- <enum-list-editable-cell @click.stop="$emit('enableedit')" v-else-if="isEnum && selected" :value="value" :column="column"></enum-list-editable-cell>-->
<enum-cell v-else-if="isEnum" :value="value" :column="column" @click.stop="$emit('enableedit')" /> <enum-cell v-else-if="isEnum" :value="value" :column="column" @click.stop="$emit('enableedit')" />
<url-cell v-else-if="isURL" :value="value" /> <url-cell v-else-if="isURL" :value="value" />
<email-cell v-else-if="isEmail" :value="value" />
<json-cell v-else-if="isJSON" :value="value" /> <json-cell v-else-if="isJSON" :value="value" />
<date-cell v-else-if="isDate" :value="value" /> <date-cell v-else-if="isDate" :value="value" />
<date-time-cell v-else-if="isDateTime" :value="value" /> <date-time-cell v-else-if="isDateTime" :value="value" />
<time-cell v-else-if="isTime" :value="value" /> <time-cell v-else-if="isTime" :value="value" />
<boolean-cell v-else-if="isBoolean" :value="value" /> <boolean-cell v-else-if="isBoolean" :value="value" read-only />
<rating-cell v-else-if="isRating" :value="value" read-only />
<span v-else :class="{'long-text-cell' : isTextArea}" :title="title">{{ value }}</span> <span v-else :class="{'long-text-cell' : isTextArea}" :title="title">{{ value }}</span>
</template> </template>
@ -30,12 +33,14 @@ import SetListCell from '~/components/project/spreadsheet/components/cell/SetLis
import EnumCell from '~/components/project/spreadsheet/components/cell/EnumCell' import EnumCell from '~/components/project/spreadsheet/components/cell/EnumCell'
import EditableAttachmentCell from '~/components/project/spreadsheet/components/editableCell/EditableAttachmentCell' import EditableAttachmentCell from '~/components/project/spreadsheet/components/editableCell/EditableAttachmentCell'
import BooleanCell from '~/components/project/spreadsheet/components/cell/BooleanCell' import BooleanCell from '~/components/project/spreadsheet/components/cell/BooleanCell'
import EmailCell from '~/components/project/spreadsheet/components/cell/EmailCell'
import RatingCell from '~/components/project/spreadsheet/components/editableCell/RatingCell'
export default { export default {
name: 'TableCell', name: 'TableCell',
components: { TimeCell, DateTimeCell, DateCell, JsonCell, UrlCell, EditableAttachmentCell, EnumCell, SetListCell, BooleanCell }, components: { RatingCell, EmailCell, TimeCell, DateTimeCell, DateCell, JsonCell, UrlCell, EditableAttachmentCell, EnumCell, SetListCell, BooleanCell },
mixins: [cell], mixins: [cell],
props: ['value', 'dbAlias', 'isLocked', 'selected'], props: ['value', 'dbAlias', 'isLocked', 'selected', 'column'],
computed: { computed: {
title() { title() {
if (typeof this.value === 'string') { return this.value } if (typeof this.value === 'string') { return this.value }

38
packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue

@ -137,6 +137,21 @@
@input="newColumn.altered = newColumn.altered || 2" @input="newColumn.altered = newColumn.altered || 2"
/> />
</v-col> </v-col>
<v-col v-else-if="isRating" cols="12">
<rating-options
v-model="newColumn.meta"
:column="newColumn"
:meta="meta"
/>
</v-col>
<v-col v-else-if="isCheckbox" cols="12">
<checkbox-options
v-model="newColumn.meta"
:column="newColumn"
:meta="meta"
/>
</v-col>
<v-col <v-col
v-if="accordion" v-if="accordion"
cols="12" cols="12"
@ -160,6 +175,10 @@
<v-col v-show="advanceOptions || !accordion" cols="12"> <v-col v-show="advanceOptions || !accordion" cols="12">
<v-row> <v-row>
<v-col v-if="newColumn.meta && columnToValidate.includes(newColumn.uidt)" cols="12" class="pt-0 pb-0">
<v-checkbox v-model="newColumn.meta.validate" dense hide-details :label="`Accept only valid ${newColumn.uidt}`" class="mt-0" />
</v-col>
<template v-if="newColumn.uidt !== 'Formula'"> <template v-if="newColumn.uidt !== 'Formula'">
<v-col v-if="isLookup" cols="12"> <v-col v-if="isLookup" cols="12">
<lookup-options <lookup-options
@ -526,10 +545,15 @@ import RelationOptions from '~/components/project/spreadsheet/components/editCol
import DlgLabelSubmitCancel from '~/components/utils/DlgLabelSubmitCancel' import DlgLabelSubmitCancel from '~/components/utils/DlgLabelSubmitCancel'
import LinkedToAnotherOptions from '~/components/project/spreadsheet/components/editColumn/LinkedToAnotherOptions' import LinkedToAnotherOptions from '~/components/project/spreadsheet/components/editColumn/LinkedToAnotherOptions'
import { validateColumnName } from '~/helpers' import { validateColumnName } from '~/helpers'
import RatingOptions from '~/components/project/spreadsheet/components/editColumn/RatingOptions'
import CheckboxOptions from '~/components/project/spreadsheet/components/editColumn/checkboxOptions'
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
export default { export default {
name: 'EditColumn', name: 'EditColumn',
components: { components: {
CheckboxOptions,
RatingOptions,
RollupOptions, RollupOptions,
FormulaOptions, FormulaOptions,
LookupOptions, LookupOptions,
@ -548,6 +572,7 @@ export default {
value: Boolean value: Boolean
}, },
data: () => ({ data: () => ({
columnToValidate,
valid: false, valid: false,
relationDeleteDlg: false, relationDeleteDlg: false,
newColumn: {}, newColumn: {},
@ -594,6 +619,12 @@ export default {
isLookup() { isLookup() {
return this.newColumn && this.newColumn.uidt === 'Lookup' return this.newColumn && this.newColumn.uidt === 'Lookup'
}, },
isRating() {
return this.newColumn && this.newColumn.uidt === UITypes.Rating
},
isCheckbox() {
return this.newColumn && this.newColumn.uidt === UITypes.Checkbox
},
isRollup() { isRollup() {
return this.newColumn && this.newColumn.uidt === 'Rollup' return this.newColumn && this.newColumn.uidt === 'Rollup'
}, },
@ -734,6 +765,7 @@ export default {
const colProp = this.sqlUi.getDataTypeForUiType(this.newColumn) const colProp = this.sqlUi.getDataTypeForUiType(this.newColumn)
this.newColumn = { this.newColumn = {
...this.newColumn, ...this.newColumn,
meta: null,
rqd: false, rqd: false,
pk: false, pk: false,
ai: false, ai: false,
@ -759,6 +791,12 @@ export default {
this.newColumn.dtxp = this.column.dtxp this.newColumn.dtxp = this.column.dtxp
} }
if (columnToValidate.includes(this.newColumn.uidt)) {
this.newColumn.meta = {
validate: this.newColumn.meta && this.newColumn.meta.validate
}
}
this.newColumn.altered = this.newColumn.altered || 2 this.newColumn.altered = this.newColumn.altered || 2
}, },
focusInput() { focusInput() {

19
packages/nc-gui/components/project/spreadsheet/components/EditableCell.vue

@ -23,9 +23,22 @@
v-on="$listeners" v-on="$listeners"
/> />
<rating-cell
v-if="isRating"
v-model="localState"
:active="active"
:is-form="isForm"
:column="column"
:is-public-grid="isPublic && !isForm"
:is-public-form="isPublic && isForm"
:is-locked="isLocked"
v-on="$listeners"
/>
<boolean-cell <boolean-cell
v-else-if="isBoolean" v-else-if="isBoolean"
v-model="localState" v-model="localState"
:column="column"
:is-form="isForm" :is-form="isForm"
v-on="parentListeners" v-on="parentListeners"
/> />
@ -132,10 +145,12 @@ import EditableAttachmentCell from '~/components/project/spreadsheet/components/
import EnumCell from '~/components/project/spreadsheet/components/cell/EnumCell' import EnumCell from '~/components/project/spreadsheet/components/cell/EnumCell'
import SetListEditableCell from '~/components/project/spreadsheet/components/editableCell/SetListEditableCell' import SetListEditableCell from '~/components/project/spreadsheet/components/editableCell/SetListEditableCell'
import SetListCell from '~/components/project/spreadsheet/components/cell/SetListCell' import SetListCell from '~/components/project/spreadsheet/components/cell/SetListCell'
import RatingCell from '~/components/project/spreadsheet/components/editableCell/RatingCell'
export default { export default {
name: 'EditableCell', name: 'EditableCell',
components: { components: {
RatingCell,
JsonEditableCell, JsonEditableCell,
EditableUrlCell, EditableUrlCell,
SetListCell, SetListCell,
@ -182,7 +197,7 @@ export default {
if (val !== this.value) { if (val !== this.value) {
this.changed = true this.changed = true
this.$emit('input', val) this.$emit('input', val)
if (this.isAttachment || this.isEnum || this.isBoolean || this.isSet || this.isTime || this.isDateTime || this.isDate) { if (this.isAttachment || this.isEnum || this.isBoolean || this.isRating || this.isSet || this.isTime || this.isDateTime || this.isDate) {
this.syncData() this.syncData()
} else if (!this.isCurrency) { } else if (!this.isCurrency) {
this.syncDataDebounce(this) this.syncDataDebounce(this)
@ -213,7 +228,7 @@ export default {
// this.$refs.input.focus(); // this.$refs.input.focus();
}, },
beforeDestroy() { beforeDestroy() {
if (this.changed && !(this.isAttachment || this.isEnum || this.isBoolean || this.isSet || this.isTime || this.isDateTime)) { if (this.changed && !(this.isAttachment || this.isEnum || this.isBoolean || this.isRating || this.isSet || this.isTime || this.isDateTime)) {
this.changed = false this.changed = false
this.$emit('change') this.$emit('change')
} }

22
packages/nc-gui/components/project/spreadsheet/components/cell/EmailCell.vue

@ -0,0 +1,22 @@
<template>
<a v-if="isEmail" :href="`mailto:${value}`" target="_blank">{{ value }}</a>
<span v-else>{{ value }}</span>
</template>
<script>
import { isEmail } from '~/helpers'
export default {
name: 'EmailCell',
props: ['value'],
computed: {
isEmail() {
return isEmail(this.value || '')
}
}
}
</script>
<style scoped>
</style>

14
packages/nc-gui/components/project/spreadsheet/components/cell/UrlCell.vue

@ -1,12 +1,20 @@
<template> <template>
<a v-if="value" :href="value" target="_blank">{{ value }}</a> <a v-if="isValid" :href="value" target="_blank">{{ value }}</a>
<span v-else /> <span v-else>{{ value }}</span>
</template> </template>
<script> <script>
import { isValidURL } from '~/helpers'
export default { export default {
name: 'UrlCell', name: 'UrlCell',
props: ['value'] props: ['value'],
computed: {
isValid() {
return this.value && isValidURL(this.value)
}
}
} }
</script> </script>

98
packages/nc-gui/components/project/spreadsheet/components/editColumn/RatingOptions.vue

@ -0,0 +1,98 @@
<template>
<div>
<div class="nc-rating-wrapper ui-type">
<v-select
v-model="colMeta.icon"
label="Icon"
:items="icons"
dense
outlined
class="caption"
:item-value="v=>v"
:value-comparator="(a,b) => a && b && a.full === b.full && a.empty === b.empty"
>
<template #item="{ item }">
<v-icon small :color="colMeta.color">
{{ item.full }}
</v-icon>
<v-icon class="ml-2" small :color="colMeta.color">
{{ item.empty }}
</v-icon>
</template>
<template #selection="{ item }">
<v-icon small :color="colMeta.color">
{{ item.full }}
</v-icon>
<v-icon class="ml-2" small :color="colMeta.color">
{{ item.empty }}
</v-icon>
</template>
</v-select>
<v-select
v-model="colMeta.max"
label="Max"
:items="[1,2,3,4,5,6,7,8,9,10]"
dense
outlined
class="caption"
/>
</div>
<v-color-picker
v-model="colMeta.color"
class="mx-auto"
hide-inputs
/>
</div>
</template>
<script>
export default {
name: 'RatingOptions',
props: ['column', 'meta', 'value'],
data: () => ({
colMeta: {
icon: {
full: 'mdi-star',
empty: 'mdi-star-outline'
},
color: '#fcb401',
max: 5
},
icons: [{
full: 'mdi-star',
empty: 'mdi-star-outline'
}, {
full: 'mdi-heart',
empty: 'mdi-heart-outline'
}, {
full: 'mdi-moon-full',
empty: 'mdi-moon-new'
}, {
full: 'mdi-thumb-up',
empty: 'mdi-thumb-up-outline'
}, {
full: 'mdi-flag',
empty: 'mdi-flag-outline'
}]
}),
watch: {
value() {
this.colMeta = this.value || {}
},
colMeta(v) {
this.$emit('input', v)
}
},
created() {
this.colMeta = this.value || { ...this.colMeta }
}
}
</script>
<style scoped lang="scss">
.nc-rating-wrapper {
display: flex;
gap: 16px
}
</style>

95
packages/nc-gui/components/project/spreadsheet/components/editColumn/checkboxOptions.vue

@ -0,0 +1,95 @@
<template>
<div>
<div class="nc-rating-wrapper ui-type">
<v-select
v-model="colMeta.icon"
label="Icon"
:items="icons"
dense
outlined
class="caption"
:item-value="v=>v"
:value-comparator="(a,b) => a && b && a.checked === b.checked && a.unchecked === b.unchecked"
>
<template #item="{ item }">
<v-icon small :color="colMeta.color">
{{ item.checked }}
</v-icon>
<v-icon class="ml-2" small :color="colMeta.color">
{{ item.unchecked }}
</v-icon>
</template>
<template #selection="{ item }">
<v-icon small :color="colMeta.color">
{{ item.checked }}
</v-icon>
<v-icon class="ml-2" small :color="colMeta.color">
{{ item.unchecked }}
</v-icon>
</template>
</v-select>
</div>
<v-color-picker
v-model="colMeta.color"
class="mx-auto"
hide-inputs
/>
</div>
</template>
<script>
export default {
name: 'CheckboxOptions',
props: ['column', 'meta', 'value'],
data: () => ({
colMeta: {
icon: {
checked: 'mdi-check-bold',
unchecked: 'mdi-crop-square'
},
color: '#777'
},
icons: [{
checked: 'mdi-check-bold',
unchecked: 'mdi-crop-square'
}, {
checked: 'mdi-check-circle-outline',
unchecked: 'mdi-checkbox-blank-circle-outline'
}, {
checked: 'mdi-star',
unchecked: 'mdi-star-outline'
}, {
checked: 'mdi-heart',
unchecked: 'mdi-heart-outline'
}, {
checked: 'mdi-moon-full',
unchecked: 'mdi-moon-new'
}, {
checked: 'mdi-thumb-up',
unchecked: 'mdi-thumb-up-outline'
}, {
checked: 'mdi-flag',
unchecked: 'mdi-flag-outline'
}]
}),
watch: {
value() {
this.colMeta = this.value || {}
},
colMeta(v) {
this.$emit('input', v)
}
},
created() {
this.colMeta = this.value || { ...this.colMeta }
}
}
</script>
<style scoped lang="scss">
.nc-rating-wrapper {
display: flex;
gap: 16px
}
</style>

36
packages/nc-gui/components/project/spreadsheet/components/editableCell/BooleanCell.vue

@ -1,6 +1,8 @@
<template> <template>
<div class="d-flex align-center " :class="{'justify-center':!isForm}"> <div class="d-flex align-center " :class="{'justify-center':!isForm,'nc-cell-hover-show': !localState}">
<input v-model="localState" type="checkbox" v-on="parentListeners"> <v-icon small :color="checkboxMeta.color" @click="toggle">
{{ localState ? checkedIcon :uncheckedIcon }}
</v-icon>
</div> </div>
</template> </template>
@ -8,10 +10,19 @@
export default { export default {
name: 'BooleanCell', name: 'BooleanCell',
props: { props: {
column: Object,
value: [String, Number, Boolean], value: [String, Number, Boolean],
isForm: Boolean isForm: Boolean,
readOnly: Boolean
}, },
computed: { computed: {
checkedIcon() {
return (this.checkboxMeta && this.checkboxMeta.icon && this.checkboxMeta.icon.checked) || 'mdi-check-bold'
},
uncheckedIcon() {
return (this.checkboxMeta && this.checkboxMeta.icon && this.checkboxMeta.icon.unchecked) || 'mdi-crop-square'
},
localState: { localState: {
get() { get() {
return this.value return this.value
@ -20,14 +31,27 @@ export default {
this.$emit('input', val) this.$emit('input', val)
} }
}, },
parentListeners() { parentListeners() {
const $listeners = {} const $listeners = {}
return $listeners return $listeners
},
checkboxMeta() {
return {
icon: {
checked: 'mdi-check-circle-outline',
unchecked: 'mdi-checkbox-blank-circle-outline'
},
color: 'primary',
...(this.column && this.column.meta
? this.column.meta
: {})
}
} }
}, },
mounted() { methods: {
this.$el.focus() toggle() {
this.localState = !this.localState
}
} }
} }
</script> </script>

9
packages/nc-gui/components/project/spreadsheet/components/editableCell/EditableUrlCell.vue

@ -8,7 +8,8 @@ import { isValidURL } from '@/helpers'
export default { export default {
name: 'EditableUrlCell', name: 'EditableUrlCell',
props: { props: {
value: String value: String,
column: Object
}, },
computed: { computed: {
localState: { localState: {
@ -16,7 +17,11 @@ export default {
return this.value return this.value
}, },
set(val) { set(val) {
if (isValidURL(val)) { this.$emit('input', val) } if (!(
this.column &&
this.column.meta &&
this.column.meta.validate
) || isValidURL(val)) { this.$emit('input', val) }
} }
}, },
parentListeners() { parentListeners() {

71
packages/nc-gui/components/project/spreadsheet/components/editableCell/RatingCell.vue

@ -0,0 +1,71 @@
<template>
<div class="d-100 h-100" :class="{'nc-cell-hover-show': localState == 0 || !localState}">
<v-rating
v-model="localState"
:length="ratingMeta.max"
dense
x-small
:disabled="readOnly"
clearable
>
<template #item="{isFilled, click}">
<v-icon v-if="isFilled" :size="15" :color="ratingMeta.color" @click="click">
{{ fullIcon }}
</v-icon>
<v-icon
v-else
:color="ratingMeta.color"
:size="15"
class="nc-cell-hover-show"
@click="click"
>
{{ emptyIcon }}
</v-icon>
</template>
</v-rating>
</div>
</template>
<script>
export default {
name: 'RatingCell',
props: {
column: Object,
value: [String, Number],
readOnly: Boolean
},
computed: {
fullIcon() {
return (this.ratingMeta && this.ratingMeta.icon && this.ratingMeta.icon.full) || 'mdi-star'
},
emptyIcon() {
return (this.ratingMeta && this.ratingMeta.icon && this.ratingMeta.icon.empty) || 'mdi-star-outline'
},
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
ratingMeta() {
return {
icon: {
full: 'mdi-star',
empty: 'mdi-star-outline'
},
color: '#fcb401',
max: 5,
...(this.column && this.column.meta
? this.column.meta
: {})
}
}
}
}
</script>
<style scoped>
</style>

6
packages/nc-gui/components/project/spreadsheet/mixins/cell.js

@ -56,9 +56,15 @@ export default {
isURL() { isURL() {
return this.uiDatatype === 'URL' return this.uiDatatype === 'URL'
}, },
isEmail() {
return this.uiDatatype === UITypes.Email
},
isAttachment() { isAttachment() {
return this.uiDatatype === 'Attachment' return this.uiDatatype === 'Attachment'
}, },
isRating() {
return this.uiDatatype === UITypes.Rating
},
isCurrency() { isCurrency() {
return this.column.uidt == 'Currency' return this.column.uidt == 'Currency'
} }

70
packages/nc-gui/components/project/spreadsheet/views/GridView.vue

@ -139,7 +139,7 @@
v-show="!rowMeta || !rowMeta.selected" v-show="!rowMeta || !rowMeta.selected"
class="ml-2 grey--text" class="ml-2 grey--text"
:class="{ 'row-no': !isPublicView }" :class="{ 'row-no': !isPublicView }"
>{{ row + 1 }}</span> >{{ row + 1 }}</span>
<template v-if="!isPublicView"> <template v-if="!isPublicView">
<v-checkbox <v-checkbox
@ -632,31 +632,30 @@ export default {
break break
// delete // delete
case 46: case 46: {
{ if (this.editEnabled.col != null && this.editEnabled.row != null) {
if (this.editEnabled.col != null && this.editEnabled.row != null) { return
return }
}
const rowObj = this.data[this.selected.row].row
const columnObj = this.availableColumns[this.selected.col]
if ( const rowObj = this.data[this.selected.row].row
// this.isRequired(columnObj, rowObj, true) || const columnObj = this.availableColumns[this.selected.col]
columnObj.virtual
) {
return
}
this.$set(rowObj, columnObj.title, null) if (
// update/save cell value // this.isRequired(columnObj, rowObj, true) ||
this.onCellValueChange( columnObj.virtual
this.selected.col, ) {
this.selected.row, return
columnObj,
true
)
} }
this.$set(rowObj, columnObj.title, null)
// update/save cell value
this.onCellValueChange(
this.selected.col,
this.selected.row,
columnObj,
true
)
}
break break
// left // left
case 37: case 37:
@ -735,11 +734,11 @@ export default {
onClickOutside() { onClickOutside() {
if ( if (
(this.meta.columns && (this.meta.columns &&
this.meta.columns[this.selected.col] && this.meta.columns[this.selected.col] &&
this.meta.columns[this.selected.col].virtual) || this.meta.columns[this.selected.col].virtual) ||
(this.availableColumns && (this.availableColumns &&
this.availableColumns[this.editEnabled.col] && this.availableColumns[this.editEnabled.col] &&
this.availableColumns[this.editEnabled.col].uidt === 'JSON') this.availableColumns[this.editEnabled.col].uidt === 'JSON')
) { ) {
return return
} }
@ -820,6 +819,7 @@ export default {
(column && column.uidt === UITypes.DateTime) || (column && column.uidt === UITypes.DateTime) ||
(column && column.uidt === UITypes.Date) || (column && column.uidt === UITypes.Date) ||
(column && column.uidt === UITypes.Time) || (column && column.uidt === UITypes.Time) ||
(column && column.uidt === UITypes.Rating) ||
(this.sqlUi && (this.sqlUi &&
column.dt && column.dt &&
this.sqlUi.getAbstractType(column) === 'boolean') this.sqlUi.getAbstractType(column) === 'boolean')
@ -930,7 +930,7 @@ export default {
} }
.search-field.v-text-field.v-text-field--solo.v-input--dense .search-field.v-text-field.v-text-field--solo.v-input--dense
> .v-input__control { > .v-input__control {
min-height: auto; min-height: auto;
} }
@ -1163,4 +1163,18 @@ td:first-child {
.has-many-icon { .has-many-icon {
transform: rotate(90deg); transform: rotate(90deg);
} }
::v-deep {
tr {
.nc-cell-hover-show {
opacity: 0;
transition: .3s opacity;
}
&:hover .nc-cell-hover-show {
opacity: .7;
}
}
}
</style> </style>

64
packages/nc-gui/lang/zh_CN.json

@ -38,7 +38,7 @@
"signIn": "登录", "signIn": "登录",
"signOut": "登出", "signOut": "登出",
"required": "必填项", "required": "必填项",
"preferred": "Preferred", "preferred": "首选",
"mandatory": "强制的", "mandatory": "强制的",
"loading": "加载中...", "loading": "加载中...",
"title": "标题", "title": "标题",
@ -59,7 +59,7 @@
"objects": { "objects": {
"project": "项目", "project": "项目",
"projects": "项目", "projects": "项目",
"table": "格", "table": "格",
"tables": "表格", "tables": "表格",
"field": "字段", "field": "字段",
"fields": "字段", "fields": "字段",
@ -74,7 +74,7 @@
"view": "视图", "view": "视图",
"views": "视图", "views": "视图",
"viewType": { "viewType": {
"grid": "格", "grid": "格",
"gallery": "画廊", "gallery": "画廊",
"form": "表单", "form": "表单",
"kanban": "看板", "kanban": "看板",
@ -154,7 +154,7 @@
"teamAndAuth": "团队和认证", "teamAndAuth": "团队和认证",
"rolesUserMgmt": "角色和用户管理", "rolesUserMgmt": "角色和用户管理",
"userMgmt": "用户账号管理", "userMgmt": "用户账号管理",
"apiTokenMgmt": "API 令牌管理", "apiTokenMgmt": "API Tokens 管理",
"rolesMgmt": "角色管理", "rolesMgmt": "角色管理",
"projMeta": "项目基础信息", "projMeta": "项目基础信息",
"metaMgmt": "项目基础信息管理", "metaMgmt": "项目基础信息管理",
@ -170,12 +170,12 @@
"headCreateProject": "新建项目 | NocoDB", "headCreateProject": "新建项目 | NocoDB",
"headLogin": "登录 | NocoDB", "headLogin": "登录 | NocoDB",
"resetPassword": "重置密码", "resetPassword": "重置密码",
"teamAndSettings": "Team & Settings", "teamAndSettings": "团队和设置",
"apiDocs": "API Docs", "apiDocs": "API 文档",
"importFromAirtable": "Import From Airtable" "importFromAirtable": "Import From Airtable"
}, },
"labels": { "labels": {
"notifyVia": "通知Via", "notifyVia": "通知通过",
"projName": "项目名", "projName": "项目名",
"tableName": "表名称", "tableName": "表名称",
"viewName": "查看名称", "viewName": "查看名称",
@ -192,7 +192,7 @@
"port": "端口号", "port": "端口号",
"username": "用户名", "username": "用户名",
"password": "密码", "password": "密码",
"schemaName": "Schema name", "schemaName": "架构名称",
"action": "行动", "action": "行动",
"actions": "行动", "actions": "行动",
"operation": "操作", "operation": "操作",
@ -201,7 +201,7 @@
"description": "描述", "description": "描述",
"authentication": "验证", "authentication": "验证",
"token": "令牌", "token": "令牌",
"where": "where", "where": "在哪里",
"cache": "缓存", "cache": "缓存",
"chat": "聊天", "chat": "聊天",
"email": "电子邮件", "email": "电子邮件",
@ -225,15 +225,15 @@
}, },
"community": { "community": {
"starUs1": "点赞", "starUs1": "点赞",
"starUs2": "我们 Github", "starUs2": "我们 Github",
"bookDemo": "查看免费演示", "bookDemo": "查看免费演示",
"getAnswered": "通过这里让你的问题得到解答", "getAnswered": "通过这里让你的问题得到解答",
"joinDiscord": "加入 Discord", "joinDiscord": "加入 Discord",
"joinCommunity": "Join NocoDB Community", "joinCommunity": "加入 NocoDB 社区",
"joinReddit": "加入/r/NocoDB", "joinReddit": "加入 /r/NocoDB",
"followNocodb": "关注 NocoDB" "followNocodb": "关注 NocoDB"
}, },
"docReference": "文件参考文献", "docReference": "参考文档",
"selectUserRole": "选择用户角色", "selectUserRole": "选择用户角色",
"childTable": "子表", "childTable": "子表",
"childColumn": "子列", "childColumn": "子列",
@ -256,7 +256,7 @@
"excel": "从Excel创建项目", "excel": "从Excel创建项目",
"template": "从模板创建项目" "template": "从模板创建项目"
}, },
"OkSaveProject": "好的和保存项目", "OkSaveProject": "确认并保存项目",
"upgrade": { "upgrade": {
"available": "升级可用", "available": "升级可用",
"releaseNote": "发行说明", "releaseNote": "发行说明",
@ -265,7 +265,7 @@
"translate": "帮助翻译", "translate": "帮助翻译",
"account": { "account": {
"authToken": "复制auth令牌", "authToken": "复制auth令牌",
"swagger": "Swagger Apis Doc.", "swagger": "Swagger Apis 文档",
"projInfo": "复制项目信息", "projInfo": "复制项目信息",
"themes": "主题" "themes": "主题"
}, },
@ -287,7 +287,7 @@
"editUser": "编辑用户", "editUser": "编辑用户",
"deleteUser": "从项目中删除用户", "deleteUser": "从项目中删除用户",
"resendInvite": "重新发送邀请电子邮件", "resendInvite": "重新发送邀请电子邮件",
"copyInviteURL": "复制邀请URL.", "copyInviteURL": "复制邀请链接",
"newRole": "新角色", "newRole": "新角色",
"reloadRoles": "重新加载角色", "reloadRoles": "重新加载角色",
"nextPage": "下一页", "nextPage": "下一页",
@ -306,10 +306,10 @@
"insertRow": "插入新行", "insertRow": "插入新行",
"deleteRow": "删除行", "deleteRow": "删除行",
"deleteSelectedRow": "删除所选行", "deleteSelectedRow": "删除所选行",
"importExcel": "导入Excel.", "importExcel": "导入Excel",
"importCSV": "Import CSV", "importCSV": "Import CSV",
"downloadCSV": "下载为CSV.", "downloadCSV": "下载为CSV",
"uploadCSV": "上传CSV.", "uploadCSV": "上传CSV",
"import": "导入", "import": "导入",
"importMetadata": "导入元数据", "importMetadata": "导入元数据",
"exportMetadata": "导出元数据", "exportMetadata": "导出元数据",
@ -326,16 +326,16 @@
"createGrid": "创建网格视图", "createGrid": "创建网格视图",
"createGallery": "创建画廊视图", "createGallery": "创建画廊视图",
"createCalendar": "创建日历视图", "createCalendar": "创建日历视图",
"createKanban": "创建寻呼视图", "createKanban": "创建看板视图",
"createForm": "创建表单视图", "createForm": "创建表单视图",
"showSystemFields": "显示系统字段", "showSystemFields": "显示系统字段",
"copyUrl": "复制URL.", "copyUrl": "复制链接",
"openTab": "打开新标签", "openTab": "打开新标签",
"iFrame": "复制嵌入HTML代码", "iFrame": "复制嵌入HTML代码",
"addWebhook": "添加新的webhook.", "addWebhook": "添加新的webhook.",
"newToken": "添加新令牌", "newToken": "添加新 Token",
"exportZip": "导出为zip格式", "exportZip": "导出为zip格式",
"importZip": "zip格式导入", "importZip": "导入zip格式",
"metaSync": "立即同步", "metaSync": "立即同步",
"settings": "设置", "settings": "设置",
"previewAs": "预览", "previewAs": "预览",
@ -348,7 +348,7 @@
}, },
"tooltip": { "tooltip": {
"saveChanges": "保存更改", "saveChanges": "保存更改",
"xcDB": "新建一个项目", "xcDB": "新建项目",
"extDB": "支持 MySQL、PostgreSQL、SQL Server 和 SQLite", "extDB": "支持 MySQL、PostgreSQL、SQL Server 和 SQLite",
"apiRest": "通过 REST APIs 访问", "apiRest": "通过 REST APIs 访问",
"apiGQL": "通过 GraphQL APIs 访问", "apiGQL": "通过 GraphQL APIs 访问",
@ -365,7 +365,7 @@
"reloadList": "重新加载列表", "reloadList": "重新加载列表",
"metaSync": "同步元数据", "metaSync": "同步元数据",
"sqlMigration": "重新加载迁移", "sqlMigration": "重新加载迁移",
"updateRestart": "更新 & 重启", "updateRestart": "更新重启",
"cancelReturn": "取消并返回", "cancelReturn": "取消并返回",
"exportMetadata": "从元数据表格导出所有元数据到元数据目录", "exportMetadata": "从元数据表格导出所有元数据到元数据目录",
"importMetadata": "从元数据目录导入所有的元数据到元数据表格", "importMetadata": "从元数据目录导入所有的元数据到元数据表格",
@ -401,7 +401,7 @@
"excelURL": "输入Excel文件URL", "excelURL": "输入Excel文件URL",
"csvURL": "Enter CSV file URL", "csvURL": "Enter CSV file URL",
"footMsg": "要解析为推断数据类型的行数", "footMsg": "要解析为推断数据类型的行数",
"excelImport": "板材可用于进口", "excelImport": "可用于导入的表格",
"exportMetadata": "您想从元表导出元数据吗?", "exportMetadata": "您想从元表导出元数据吗?",
"importMetadata": "您想从元表导入元数据吗?", "importMetadata": "您想从元表导入元数据吗?",
"clearMetadata": "你想清除元表中的元数据吗?", "clearMetadata": "你想清除元表中的元数据吗?",
@ -410,7 +410,7 @@
"startProject": "你想启动这个项目吗?", "startProject": "你想启动这个项目吗?",
"restartProject": "你想重新启动项目吗?", "restartProject": "你想重新启动项目吗?",
"deleteProject": "你想删除这个项目吗?", "deleteProject": "你想删除这个项目吗?",
"shareBasePrivate": "产生公开可享的Readonly Base", "shareBasePrivate": "产生公开共享的只读基础",
"shareBasePublic": "互联网上的任何人都可以查看", "shareBasePublic": "互联网上的任何人都可以查看",
"userInviteNoSMTP": "看起来你还没有配置邮件!请复制上面的邀请链接并将其发送给", "userInviteNoSMTP": "看起来你还没有配置邮件!请复制上面的邀请链接并将其发送给",
"dragDropHide": "在此处拖放字段以隐藏", "dragDropHide": "在此处拖放字段以隐藏",
@ -485,7 +485,7 @@
"tableNameInDb": "数据库中保存的表名" "tableNameInDb": "数据库中保存的表名"
}, },
"error": { "error": {
"searchProject": "你的搜索: {search} 没有发现匹配的结果", "searchProject": "搜索: {search} 没有发现匹配的结果",
"invalidChar": "文件夹路径中的字符无效。", "invalidChar": "文件夹路径中的字符无效。",
"invalidDbCredentials": "无效的数据库凭据。", "invalidDbCredentials": "无效的数据库凭据。",
"unableToConnectToDb": "无法连接到数据库,请检查您的数据库是否已启动。", "unableToConnectToDb": "无法连接到数据库,请检查您的数据库是否已启动。",
@ -503,14 +503,14 @@
"exportMetadata": "项目元数据成功导出", "exportMetadata": "项目元数据成功导出",
"importMetadata": "项目元数据成功导入", "importMetadata": "项目元数据成功导入",
"clearMetadata": "项目元数据已成功清除", "clearMetadata": "项目元数据已成功清除",
"stopProject": "项目成功停止", "stopProject": "项目成功停止",
"startProject": "项目成功开始", "startProject": "项目成功开始",
"restartProject": "项目成功重新启动", "restartProject": "项目成功重新启动",
"deleteProject": "项目已成功删除", "deleteProject": "项目已成功删除",
"authToken": "验证令牌复制到剪贴板", "authToken": "验证令牌复制到剪贴板",
"projInfo": "将项目信息复制到剪贴板", "projInfo": "将项目信息复制到剪贴板",
"inviteUrlCopy": "复制邀请URL到剪贴板", "inviteUrlCopy": "复制邀请链接到剪贴板",
"createView": "查看成功创建", "createView": "视图创建成功",
"formEmailSMTP": "请激活App Store中的SMTP插件以启用电子邮件通知", "formEmailSMTP": "请激活App Store中的SMTP插件以启用电子邮件通知",
"collabView": "成功转换为协作视图", "collabView": "成功转换为协作视图",
"lockedView": "成功转换为锁定视图", "lockedView": "成功转换为锁定视图",

24
packages/noco-docs/content/en/setup-and-usages/import-airtable-to-sql-database-within-a-minute-for-free.md

@ -1,4 +1,3 @@
---
title: 'Import: Airtable to NocoDB' title: 'Import: Airtable to NocoDB'
description: 'Import: Airtable to NocoDB' description: 'Import: Airtable to NocoDB'
position: 1150 position: 1150
@ -9,7 +8,26 @@ menuTitle: 'Import: Airtable to NocoDB'
## Import Airtable to NocoDB ## Import Airtable to NocoDB
### Find & enter your Airtable API Key ### Find & enter your Airtable API Key
- TODO Copy API Key from [Airtable Accounts](https://airtable.com/account) page
![Screenshot 2022-05-16 at 1 50 07 PM](https://user-images.githubusercontent.com/86527202/168569905-48c16d6d-c44a-4337-be49-0ac3dc1f7b75.png)
### Share you Airtable base ### Share you Airtable base
- TODO Detailed procedure is captured [here](https://support.airtable.com/hc/en-us/articles/205752117-Creating-a-base-share-link-or-a-view-share-link#basesharelink)
Quick steps
1. Open `Share` menu in your Project/ Base
![Screenshot 2022-05-16 at 3 47 27 PM](https://user-images.githubusercontent.com/86527202/168572054-533b8c19-d76e-4add-b876-f1e0570ac33c.png)
3. Open tab `Share Publicly`
4. Enable `Turn on full base access`
5. Copy generated shared base URL
![Screenshot 2022-05-16 at 3 41 54 PM](https://user-images.githubusercontent.com/86527202/168572062-5dee065d-2394-426d-8f43-77ecc0c9b73f.png)

84
packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts

@ -1,4 +1,4 @@
import UITypes from "../UITypes"; import UITypes from '../UITypes';
const dbTypes = [ const dbTypes = [
'bigint', 'bigint',
@ -35,15 +35,15 @@ const dbTypes = [
'uniqueidentifier', 'uniqueidentifier',
'varbinary', 'varbinary',
'xml', 'xml',
'varchar' 'varchar',
]; ];
export class MssqlUi { export class MssqlUi {
static getNewTableColumns() { static getNewTableColumns() {
return [ return [
{ {
column_name: 'id', column_name: 'id',
title: 'Id', title: 'Id',
dt: 'int', dt: 'int',
dtx: 'integer', dtx: 'integer',
ct: 'int(11)', ct: 'int(11)',
@ -62,11 +62,11 @@ export class MssqlUi {
altered: 1, altered: 1,
uidt: 'ID', uidt: 'ID',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'title', column_name: 'title',
title: 'Title', title: 'Title',
dt: 'varchar', dt: 'varchar',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar(45)', ct: 'varchar(45)',
@ -85,11 +85,11 @@ export class MssqlUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'created_at', column_name: 'created_at',
title: 'CreatedAt', title: 'CreatedAt',
dt: 'datetime', dt: 'datetime',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar(45)', ct: 'varchar(45)',
@ -108,11 +108,11 @@ export class MssqlUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'updated_at', column_name: 'updated_at',
title: 'UpdatedAt', title: 'UpdatedAt',
dt: 'datetime', dt: 'datetime',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar(45)', ct: 'varchar(45)',
@ -131,8 +131,8 @@ export class MssqlUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
} },
]; ];
} }
@ -157,7 +157,7 @@ export class MssqlUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}; };
} }
@ -741,13 +741,15 @@ export class MssqlUi {
} }
static extractFunctionName(query) { static extractFunctionName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
static extractProcedureName(query) { static extractProcedureName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
@ -808,7 +810,7 @@ export class MssqlUi {
const column = { const column = {
dp: null, dp: null,
tn, tn,
column_name: keys[i], column_name: keys[i],
cno: keys[i], cno: keys[i],
np: 10, np: 10,
ns: 0, ns: 0,
@ -827,7 +829,7 @@ export class MssqlUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: null, dtxp: null,
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}; };
switch (typeof json[keys[i]]) { switch (typeof json[keys[i]]) {
@ -835,13 +837,13 @@ export class MssqlUi {
if (Number.isInteger(json[keys[i]])) { if (Number.isInteger(json[keys[i]])) {
if (MssqlUi.isValidTimestamp(keys[i], json[keys[i]])) { if (MssqlUi.isValidTimestamp(keys[i], json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'timestamp' dt: 'timestamp',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'int', dt: 'int',
np: 10, np: 10,
ns: 0 ns: 0,
}); });
} }
} else { } else {
@ -850,25 +852,25 @@ export class MssqlUi {
np: 10, np: 10,
ns: 2, ns: 2,
dtxp: '11', dtxp: '11',
dtxs: 2 dtxs: 2,
}); });
} }
break; break;
case 'string': case 'string':
if (MssqlUi.isValidDate(json[keys[i]])) { if (MssqlUi.isValidDate(json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'datetime' dt: 'datetime',
}); });
} else if (json[keys[i]].length <= 255) { } else if (json[keys[i]].length <= 255) {
Object.assign(column, { Object.assign(column, {
dt: 'varchar', dt: 'varchar',
np: 255, np: 255,
ns: 0, ns: 0,
dtxp: '255' dtxp: '255',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'text' dt: 'text',
}); });
} }
break; break;
@ -876,7 +878,7 @@ export class MssqlUi {
Object.assign(column, { Object.assign(column, {
dt: 'bit', dt: 'bit',
np: null, np: null,
ns: 0 ns: 0,
}); });
break; break;
case 'object': case 'object':
@ -884,7 +886,7 @@ export class MssqlUi {
dt: 'varchar', dt: 'varchar',
np: 255, np: 255,
ns: 0, ns: 0,
dtxp: '255' dtxp: '255',
}); });
break; break;
default: default:
@ -1043,9 +1045,7 @@ export class MssqlUi {
} }
} }
static getDataTypeForUiType( static getDataTypeForUiType(col): {
col
): {
readonly dt: string; readonly dt: string;
readonly [key: string]: any; readonly [key: string]: any;
} { } {
@ -1098,7 +1098,7 @@ export class MssqlUi {
colProp.validate = { colProp.validate = {
func: ['isMobilePhone'], func: ['isMobilePhone'],
args: [''], args: [''],
msg: ['Validation failed : isMobilePhone'] msg: ['Validation failed : isMobilePhone'],
}; };
break; break;
case 'Email': case 'Email':
@ -1106,7 +1106,7 @@ export class MssqlUi {
colProp.validate = { colProp.validate = {
func: ['isEmail'], func: ['isEmail'],
args: [''], args: [''],
msg: ['Validation failed : isEmail'] msg: ['Validation failed : isEmail'],
}; };
break; break;
case 'URL': case 'URL':
@ -1114,7 +1114,7 @@ export class MssqlUi {
colProp.validate = { colProp.validate = {
func: ['isURL'], func: ['isURL'],
args: [''], args: [''],
msg: ['Validation failed : isURL'] msg: ['Validation failed : isURL'],
}; };
break; break;
case 'Number': case 'Number':
@ -1128,7 +1128,7 @@ export class MssqlUi {
colProp.validate = { colProp.validate = {
func: ['isCurrency'], func: ['isCurrency'],
args: [''], args: [''],
msg: ['Validation failed : isCurrency'] msg: ['Validation failed : isCurrency'],
}; };
break; break;
case 'Percent': case 'Percent':
@ -1138,7 +1138,7 @@ export class MssqlUi {
colProp.dt = 'int'; colProp.dt = 'int';
break; break;
case 'Rating': case 'Rating':
colProp.dt = 'float'; colProp.dt = 'int';
break; break;
case 'Formula': case 'Formula':
colProp.dt = 'varchar'; colProp.dt = 'varchar';
@ -1224,7 +1224,7 @@ export class MssqlUi {
'numeric', 'numeric',
'real', 'real',
'smallint', 'smallint',
'tinyint' 'tinyint',
]; ];
case 'Decimal': case 'Decimal':
@ -1240,7 +1240,7 @@ export class MssqlUi {
'numeric', 'numeric',
'real', 'real',
'smallint', 'smallint',
'tinyint' 'tinyint',
]; ];
case 'Percent': case 'Percent':
@ -1253,7 +1253,7 @@ export class MssqlUi {
'numeric', 'numeric',
'real', 'real',
'smallint', 'smallint',
'tinyint' 'tinyint',
]; ];
case 'Duration': case 'Duration':
@ -1266,7 +1266,7 @@ export class MssqlUi {
'numeric', 'numeric',
'real', 'real',
'smallint', 'smallint',
'tinyint' 'tinyint',
]; ];
case 'Rating': case 'Rating':
@ -1279,7 +1279,7 @@ export class MssqlUi {
'numeric', 'numeric',
'real', 'real',
'smallint', 'smallint',
'tinyint' 'tinyint',
]; ];
case 'Formula': case 'Formula':
@ -1302,7 +1302,7 @@ export class MssqlUi {
case 'LastModifiedTime': case 'LastModifiedTime':
return [ return [
'datetimeoffset', 'datetimeoffset',
'datetime2' 'datetime2',
// 'datetime' // 'datetime'
]; ];

74
packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts

@ -1,4 +1,4 @@
import UITypes from "../UITypes"; import UITypes from '../UITypes';
const dbTypes = [ const dbTypes = [
'int', 'int',
@ -39,7 +39,7 @@ const dbTypes = [
'multipoint', 'multipoint',
'multilinestring', 'multilinestring',
'multipolygon', 'multipolygon',
'json' 'json',
]; ];
export class MysqlUi { export class MysqlUi {
@ -66,7 +66,7 @@ export class MysqlUi {
altered: 1, altered: 1,
uidt: 'ID', uidt: 'ID',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'title', column_name: 'title',
@ -89,7 +89,7 @@ export class MysqlUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'created_at', column_name: 'created_at',
@ -112,7 +112,7 @@ export class MysqlUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'updated_at', column_name: 'updated_at',
@ -135,8 +135,8 @@ export class MysqlUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
} },
]; ];
} }
@ -161,7 +161,7 @@ export class MysqlUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}; };
} }
@ -659,13 +659,15 @@ export class MysqlUi {
} }
static extractFunctionName(query) { static extractFunctionName(query) {
const reg = /^\s*CREATE\s+.*?(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:`?[\w\d_]+`?\.)?`?([\w_\d]+)`?/i; const reg =
/^\s*CREATE\s+.*?(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:`?[\w\d_]+`?\.)?`?([\w_\d]+)`?/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
static extractProcedureName(query) { static extractProcedureName(query) {
const reg = /^\s*CREATE.*?\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE.*?\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
@ -677,14 +679,14 @@ export class MysqlUi {
// set headers before settings result // set headers before settings result
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const text = keys[i]; const text = keys[i];
headers.push({text, value: text, sortable: false}); headers.push({ text, value: text, sortable: false });
} }
} else { } else {
const keys = Object.keys(result); const keys = Object.keys(result);
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const text = keys[i]; const text = keys[i];
if (typeof text !== 'function') { if (typeof text !== 'function') {
headers.push({text, value: text, sortable: false}); headers.push({ text, value: text, sortable: false });
} }
} }
result = [result]; result = [result];
@ -750,7 +752,7 @@ export class MysqlUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: null, dtxp: null,
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}; };
switch (typeof json[keys[i]]) { switch (typeof json[keys[i]]) {
@ -758,13 +760,13 @@ export class MysqlUi {
if (Number.isInteger(json[keys[i]])) { if (Number.isInteger(json[keys[i]])) {
if (MysqlUi.isValidTimestamp(keys[i], json[keys[i]])) { if (MysqlUi.isValidTimestamp(keys[i], json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'timestamp' dt: 'timestamp',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'int', dt: 'int',
np: 10, np: 10,
ns: 0 ns: 0,
}); });
} }
} else { } else {
@ -773,25 +775,25 @@ export class MysqlUi {
np: 10, np: 10,
ns: 2, ns: 2,
dtxp: '11', dtxp: '11',
dtxs: 2 dtxs: 2,
}); });
} }
break; break;
case 'string': case 'string':
if (MysqlUi.isValidDate(json[keys[i]])) { if (MysqlUi.isValidDate(json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'datetime' dt: 'datetime',
}); });
} else if (json[keys[i]].length <= 255) { } else if (json[keys[i]].length <= 255) {
Object.assign(column, { Object.assign(column, {
dt: 'varchar', dt: 'varchar',
np: 255, np: 255,
ns: 0, ns: 0,
dtxp: '255' dtxp: '255',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'text' dt: 'text',
}); });
} }
break; break;
@ -799,14 +801,14 @@ export class MysqlUi {
Object.assign(column, { Object.assign(column, {
dt: 'boolean', dt: 'boolean',
np: 3, np: 3,
ns: 0 ns: 0,
}); });
break; break;
case 'object': case 'object':
Object.assign(column, { Object.assign(column, {
dt: 'json', dt: 'json',
np: 3, np: 3,
ns: 0 ns: 0,
}); });
break; break;
default: default:
@ -991,7 +993,7 @@ export class MysqlUi {
colProp.validate = { colProp.validate = {
func: ['isMobilePhone'], func: ['isMobilePhone'],
args: [''], args: [''],
msg: ['Validation failed : isMobilePhone ({cn})'] msg: ['Validation failed : isMobilePhone ({cn})'],
}; };
break; break;
case 'Email': case 'Email':
@ -999,7 +1001,7 @@ export class MysqlUi {
colProp.validate = { colProp.validate = {
func: ['isEmail'], func: ['isEmail'],
args: [''], args: [''],
msg: ['Validation failed : isEmail ({cn})'] msg: ['Validation failed : isEmail ({cn})'],
}; };
break; break;
case 'URL': case 'URL':
@ -1007,7 +1009,7 @@ export class MysqlUi {
colProp.validate = { colProp.validate = {
func: ['isURL'], func: ['isURL'],
args: [''], args: [''],
msg: ['Validation failed : isURL ({cn})'] msg: ['Validation failed : isURL ({cn})'],
}; };
break; break;
case 'Number': case 'Number':
@ -1021,7 +1023,7 @@ export class MysqlUi {
colProp.validate = { colProp.validate = {
func: ['isCurrency'], func: ['isCurrency'],
args: [''], args: [''],
msg: ['Validation failed : isCurrency'] msg: ['Validation failed : isCurrency'],
}; };
break; break;
case 'Percent': case 'Percent':
@ -1031,7 +1033,7 @@ export class MysqlUi {
colProp.dt = 'int'; colProp.dt = 'int';
break; break;
case 'Rating': case 'Rating':
colProp.dt = 'float'; colProp.dt = 'int';
break; break;
case 'Formula': case 'Formula':
colProp.dt = 'varchar'; colProp.dt = 'varchar';
@ -1089,7 +1091,7 @@ export class MysqlUi {
'text', 'text',
'tinytext', 'tinytext',
'mediumtext', 'mediumtext',
'longtext' 'longtext',
]; ];
case 'Attachment': case 'Attachment':
@ -1101,7 +1103,7 @@ export class MysqlUi {
'text', 'text',
'tinytext', 'tinytext',
'mediumtext', 'mediumtext',
'longtext' 'longtext',
]; ];
case 'JSON': case 'JSON':
@ -1116,7 +1118,7 @@ export class MysqlUi {
'bit', 'bit',
'boolean', 'boolean',
'serial', 'serial',
'tinyint' 'tinyint',
]; ];
case 'MultiSelect': case 'MultiSelect':
@ -1148,7 +1150,7 @@ export class MysqlUi {
'float', 'float',
'decimal', 'decimal',
'double', 'double',
'serial' 'serial',
]; ];
case 'Decimal': case 'Decimal':
@ -1164,7 +1166,7 @@ export class MysqlUi {
'smallint', 'smallint',
'mediumint', 'mediumint',
'bigint', 'bigint',
'bit' 'bit',
]; ];
case 'Percent': case 'Percent':
@ -1177,7 +1179,7 @@ export class MysqlUi {
'smallint', 'smallint',
'mediumint', 'mediumint',
'bigint', 'bigint',
'bit' 'bit',
]; ];
case 'Duration': case 'Duration':
@ -1190,7 +1192,7 @@ export class MysqlUi {
'smallint', 'smallint',
'mediumint', 'mediumint',
'bigint', 'bigint',
'bit' 'bit',
]; ];
case 'Rating': case 'Rating':
@ -1203,7 +1205,7 @@ export class MysqlUi {
'smallint', 'smallint',
'mediumint', 'mediumint',
'bigint', 'bigint',
'bit' 'bit',
]; ];
case 'Formula': case 'Formula':
@ -1214,7 +1216,7 @@ export class MysqlUi {
'text', 'text',
'tinytext', 'tinytext',
'mediumtext', 'mediumtext',
'longtext' 'longtext',
]; ];
case 'Rollup': case 'Rollup':
@ -1248,7 +1250,7 @@ export class MysqlUi {
'polygon', 'polygon',
'multipoint', 'multipoint',
'multilinestring', 'multilinestring',
'multipolygon' 'multipolygon',
]; ];
case 'Button': case 'Button':

36
packages/nocodb-sdk/src/lib/sqlUi/OracleUi.ts

@ -22,7 +22,7 @@ export class OracleUi {
altered: 1, altered: 1,
uidt: 'ID', uidt: 'ID',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'title', column_name: 'title',
@ -45,8 +45,8 @@ export class OracleUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
} },
// { // {
// column_name: "created_at", // column_name: "created_at",
// dt: "timestamp", // dt: "timestamp",
@ -107,7 +107,7 @@ export class OracleUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}; };
} }
@ -435,13 +435,15 @@ export class OracleUi {
} }
static extractFunctionName(query) { static extractFunctionName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
static extractProcedureName(query) { static extractProcedureName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
@ -522,7 +524,7 @@ export class OracleUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: '11', dtxp: '11',
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}); });
} else { } else {
columns.push({ columns.push({
@ -548,7 +550,7 @@ export class OracleUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: '11', dtxp: '11',
dtxs: 2, dtxs: 2,
altered: 1 altered: 1,
}); });
} }
@ -579,7 +581,7 @@ export class OracleUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: '45', dtxp: '45',
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}); });
} else { } else {
columns.push({ columns.push({
@ -605,7 +607,7 @@ export class OracleUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: null, dtxp: null,
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}); });
} }
@ -635,7 +637,7 @@ export class OracleUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: '1', dtxp: '1',
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}); });
break; break;
@ -663,7 +665,7 @@ export class OracleUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: null, dtxp: null,
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}); });
break; break;
@ -839,7 +841,7 @@ export class OracleUi {
colProp.validate = { colProp.validate = {
func: ['isMobilePhone'], func: ['isMobilePhone'],
args: [''], args: [''],
msg: ['Validation failed : isMobilePhone'] msg: ['Validation failed : isMobilePhone'],
}; };
break; break;
case 'Email': case 'Email':
@ -847,7 +849,7 @@ export class OracleUi {
colProp.validate = { colProp.validate = {
func: ['isEmail'], func: ['isEmail'],
args: [''], args: [''],
msg: ['Validation failed : isEmail'] msg: ['Validation failed : isEmail'],
}; };
break; break;
case 'URL': case 'URL':
@ -855,7 +857,7 @@ export class OracleUi {
colProp.validate = { colProp.validate = {
func: ['isURL'], func: ['isURL'],
args: [''], args: [''],
msg: ['Validation failed : isURL'] msg: ['Validation failed : isURL'],
}; };
break; break;
case 'Number': case 'Number':
@ -869,7 +871,7 @@ export class OracleUi {
colProp.validate = { colProp.validate = {
func: ['isCurrency'], func: ['isCurrency'],
args: [''], args: [''],
msg: ['Validation failed : isCurrency'] msg: ['Validation failed : isCurrency'],
}; };
break; break;
case 'Percent': case 'Percent':
@ -879,7 +881,7 @@ export class OracleUi {
colProp.dt = 'integer'; colProp.dt = 'integer';
break; break;
case 'Rating': case 'Rating':
colProp.dt = 'float'; colProp.dt = 'integer';
break; break;
case 'Formula': case 'Formula':
colProp.dt = 'varchar'; colProp.dt = 'varchar';

97
packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts

@ -1,4 +1,4 @@
import UITypes from "../UITypes"; import UITypes from '../UITypes';
const dbTypes = [ const dbTypes = [
'int', 'int',
@ -98,15 +98,15 @@ const dbTypes = [
'unknown', 'unknown',
'void', 'void',
'xid', 'xid',
'xml' 'xml',
]; ];
export class PgUi { export class PgUi {
static getNewTableColumns() { static getNewTableColumns() {
return [ return [
{ {
column_name: 'id', column_name: 'id',
title: 'Id', title: 'Id',
dt: 'int4', dt: 'int4',
dtx: 'integer', dtx: 'integer',
ct: 'int(11)', ct: 'int(11)',
@ -125,11 +125,11 @@ export class PgUi {
altered: 1, altered: 1,
uidt: 'ID', uidt: 'ID',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'title', column_name: 'title',
title: 'Title', title: 'Title',
dt: 'character varying', dt: 'character varying',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar(45)', ct: 'varchar(45)',
@ -148,11 +148,11 @@ export class PgUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'created_at', column_name: 'created_at',
title: 'CreatedAt', title: 'CreatedAt',
dt: 'timestamp', dt: 'timestamp',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar(45)', ct: 'varchar(45)',
@ -171,11 +171,11 @@ export class PgUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'updated_at', column_name: 'updated_at',
title: 'UpdatedAt', title: 'UpdatedAt',
dt: 'timestamp', dt: 'timestamp',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar(45)', ct: 'varchar(45)',
@ -195,8 +195,8 @@ export class PgUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
} },
]; ];
} }
@ -221,7 +221,7 @@ export class PgUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}; };
} }
@ -1160,13 +1160,15 @@ export class PgUi {
} }
static extractFunctionName(query) { static extractFunctionName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
static extractProcedureName(query) { static extractProcedureName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
@ -1176,8 +1178,8 @@ export class PgUi {
headers.push({ text: 'Row count', value: 'rowCount', sortable: false }); headers.push({ text: 'Row count', value: 'rowCount', sortable: false });
result = [ result = [
{ {
rowCount: result.rowCount rowCount: result.rowCount,
} },
]; ];
} else { } else {
result = result.rows; result = result.rows;
@ -1232,7 +1234,7 @@ export class PgUi {
const column = { const column = {
dp: null, dp: null,
tn, tn,
column_name: keys[i], column_name: keys[i],
cno: keys[i], cno: keys[i],
np: 10, np: 10,
ns: 0, ns: 0,
@ -1251,7 +1253,7 @@ export class PgUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: null, dtxp: null,
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}; };
switch (typeof json[keys[i]]) { switch (typeof json[keys[i]]) {
@ -1259,13 +1261,13 @@ export class PgUi {
if (Number.isInteger(json[keys[i]])) { if (Number.isInteger(json[keys[i]])) {
if (PgUi.isValidTimestamp(keys[i], json[keys[i]])) { if (PgUi.isValidTimestamp(keys[i], json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'timestamp' dt: 'timestamp',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'int', dt: 'int',
np: 10, np: 10,
ns: 0 ns: 0,
}); });
} }
} else { } else {
@ -1274,25 +1276,25 @@ export class PgUi {
np: null, np: null,
ns: null, ns: null,
dtxp: null, dtxp: null,
dtxs: null dtxs: null,
}); });
} }
break; break;
case 'string': case 'string':
if (PgUi.isValidDate(json[keys[i]])) { if (PgUi.isValidDate(json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'date' dt: 'date',
}); });
} else if (json[keys[i]].length <= 255) { } else if (json[keys[i]].length <= 255) {
Object.assign(column, { Object.assign(column, {
dt: 'character varying', dt: 'character varying',
np: null, np: null,
ns: 0, ns: 0,
dtxp: null dtxp: null,
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'text' dt: 'text',
}); });
} }
break; break;
@ -1300,14 +1302,14 @@ export class PgUi {
Object.assign(column, { Object.assign(column, {
dt: 'boolean', dt: 'boolean',
np: 3, np: 3,
ns: 0 ns: 0,
}); });
break; break;
case 'object': case 'object':
Object.assign(column, { Object.assign(column, {
dt: 'json', dt: 'json',
np: 3, np: 3,
ns: 0 ns: 0,
}); });
break; break;
default: default:
@ -1521,7 +1523,6 @@ export class PgUi {
case 'longblob': case 'longblob':
return 'blob'; return 'blob';
case 'enum': case 'enum':
return 'enum'; return 'enum';
case 'set': case 'set':
return 'set'; return 'set';
@ -1616,7 +1617,7 @@ export class PgUi {
colProp.validate = { colProp.validate = {
func: ['isMobilePhone'], func: ['isMobilePhone'],
args: [''], args: [''],
msg: ['Validation failed : isMobilePhone'] msg: ['Validation failed : isMobilePhone'],
}; };
break; break;
case 'Email': case 'Email':
@ -1624,7 +1625,7 @@ export class PgUi {
colProp.validate = { colProp.validate = {
func: ['isEmail'], func: ['isEmail'],
args: [''], args: [''],
msg: ['Validation failed : isEmail'] msg: ['Validation failed : isEmail'],
}; };
break; break;
case 'URL': case 'URL':
@ -1632,7 +1633,7 @@ export class PgUi {
colProp.validate = { colProp.validate = {
func: ['isURL'], func: ['isURL'],
args: [''], args: [''],
msg: ['Validation failed : isURL'] msg: ['Validation failed : isURL'],
}; };
break; break;
case 'Number': case 'Number':
@ -1646,7 +1647,7 @@ export class PgUi {
colProp.validate = { colProp.validate = {
func: ['isCurrency'], func: ['isCurrency'],
args: [''], args: [''],
msg: ['Validation failed : isCurrency'] msg: ['Validation failed : isCurrency'],
}; };
break; break;
case 'Percent': case 'Percent':
@ -1656,7 +1657,7 @@ export class PgUi {
colProp.dt = 'int8'; colProp.dt = 'int8';
break; break;
case 'Rating': case 'Rating':
colProp.dt = 'float8'; colProp.dt = 'smallint';
break; break;
case 'Formula': case 'Formula':
colProp.dt = 'character varying'; colProp.dt = 'character varying';
@ -1732,7 +1733,7 @@ export class PgUi {
'int8range', 'int8range',
'serial', 'serial',
'serial2', 'serial2',
'serial8' 'serial8',
]; ];
case 'MultiSelect': case 'MultiSelect':
@ -1753,7 +1754,7 @@ export class PgUi {
'timestamptz', 'timestamptz',
'timestamp with time zone', 'timestamp with time zone',
'timetz', 'timetz',
'time with time zone' 'time with time zone',
]; ];
case 'PhoneNumber': case 'PhoneNumber':
@ -1780,7 +1781,7 @@ export class PgUi {
'float8', 'float8',
'smallint', 'smallint',
'smallserial', 'smallserial',
'numeric' 'numeric',
]; ];
case 'Decimal': case 'Decimal':
@ -1802,7 +1803,7 @@ export class PgUi {
'money', 'money',
'float4', 'float4',
'float8', 'float8',
'numeric' 'numeric',
]; ];
case 'Percent': case 'Percent':
@ -1822,7 +1823,7 @@ export class PgUi {
'float8', 'float8',
'smallint', 'smallint',
'smallserial', 'smallserial',
'numeric' 'numeric',
]; ];
case 'Duration': case 'Duration':
@ -1842,7 +1843,7 @@ export class PgUi {
'float8', 'float8',
'smallint', 'smallint',
'smallserial', 'smallserial',
'numeric' 'numeric',
]; ];
case 'Rating': case 'Rating':
@ -1862,7 +1863,7 @@ export class PgUi {
'float8', 'float8',
'smallint', 'smallint',
'smallserial', 'smallserial',
'numeric' 'numeric',
]; ];
case 'Formula': case 'Formula':
@ -1884,7 +1885,7 @@ export class PgUi {
'serial2', 'serial2',
'serial8', 'serial8',
'smallint', 'smallint',
'smallserial' 'smallserial',
]; ];
case 'Lookup': case 'Lookup':
@ -1896,7 +1897,7 @@ export class PgUi {
'timestamp', 'timestamp',
'timestamp without time zone', 'timestamp without time zone',
'timestamptz', 'timestamptz',
'timestamp with time zone' 'timestamp with time zone',
]; ];
case 'DateTime': case 'DateTime':
@ -1906,7 +1907,7 @@ export class PgUi {
'timestamp', 'timestamp',
'timestamp without time zone', 'timestamp without time zone',
'timestamptz', 'timestamptz',
'timestamp with time zone' 'timestamp with time zone',
]; ];
case 'AutoNumber': case 'AutoNumber':
@ -1922,7 +1923,7 @@ export class PgUi {
'serial2', 'serial2',
'serial8', 'serial8',
'smallint', 'smallint',
'smallserial' 'smallserial',
]; ];
case 'Barcode': case 'Barcode':
@ -1937,7 +1938,7 @@ export class PgUi {
'line', 'line',
'lseg', 'lseg',
'path', 'path',
'circle' 'circle',
]; ];
case 'Button': case 'Button':

94
packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts

@ -1,4 +1,4 @@
import UITypes from "../UITypes"; import UITypes from '../UITypes';
const dbTypes = [ const dbTypes = [
'int', 'int',
@ -22,15 +22,15 @@ const dbTypes = [
'datetime', 'datetime',
'text', 'text',
'varchar', 'varchar',
'timestamp' 'timestamp',
]; ];
export class SqliteUi { export class SqliteUi {
static getNewTableColumns() { static getNewTableColumns() {
return [ return [
{ {
column_name: 'id', column_name: 'id',
title: 'Id', title: 'Id',
dt: 'integer', dt: 'integer',
dtx: 'integer', dtx: 'integer',
ct: 'int(11)', ct: 'int(11)',
@ -49,11 +49,11 @@ export class SqliteUi {
altered: 1, altered: 1,
uidt: 'ID', uidt: 'ID',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'title', column_name: 'title',
title: 'Title', title: 'Title',
dt: 'varchar', dt: 'varchar',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar', ct: 'varchar',
@ -72,11 +72,11 @@ export class SqliteUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'created_at', column_name: 'created_at',
title: 'CreatedAt', title: 'CreatedAt',
dt: 'datetime', dt: 'datetime',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar', ct: 'varchar',
@ -95,11 +95,11 @@ export class SqliteUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
}, },
{ {
column_name: 'updated_at', column_name: 'updated_at',
title: 'UpdatedAt', title: 'UpdatedAt',
dt: 'datetime', dt: 'datetime',
dtx: 'specificType', dtx: 'specificType',
ct: 'varchar', ct: 'varchar',
@ -118,8 +118,8 @@ export class SqliteUi {
altered: 1, altered: 1,
uidt: UITypes.DateTime, uidt: UITypes.DateTime,
uip: '', uip: '',
uicn: '' uicn: '',
} },
]; ];
} }
@ -144,7 +144,7 @@ export class SqliteUi {
altered: 1, altered: 1,
uidt: 'SingleLineText', uidt: 'SingleLineText',
uip: '', uip: '',
uicn: '' uicn: '',
}; };
} }
@ -498,13 +498,15 @@ export class SqliteUi {
} }
static extractFunctionName(query) { static extractFunctionName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
static extractProcedureName(query) { static extractProcedureName(query) {
const reg = /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; const reg =
/^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i;
const match = query.match(reg); const match = query.match(reg);
return match && match[1]; return match && match[1];
} }
@ -565,7 +567,7 @@ export class SqliteUi {
const column = { const column = {
dp: null, dp: null,
tn, tn,
column_name: keys[i], column_name: keys[i],
cno: keys[i], cno: keys[i],
np: null, np: null,
ns: null, ns: null,
@ -584,7 +586,7 @@ export class SqliteUi {
dtx: 'specificType', dtx: 'specificType',
dtxp: null, dtxp: null,
dtxs: 0, dtxs: 0,
altered: 1 altered: 1,
}; };
switch (typeof json[keys[i]]) { switch (typeof json[keys[i]]) {
@ -592,16 +594,16 @@ export class SqliteUi {
if (Number.isInteger(json[keys[i]])) { if (Number.isInteger(json[keys[i]])) {
if (SqliteUi.isValidTimestamp(keys[i], json[keys[i]])) { if (SqliteUi.isValidTimestamp(keys[i], json[keys[i]])) {
Object.assign(column, { Object.assign(column, {
dt: 'timestamp' dt: 'timestamp',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'integer' dt: 'integer',
}); });
} }
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'real' dt: 'real',
}); });
} }
break; break;
@ -613,24 +615,24 @@ export class SqliteUi {
// } else // } else
if (json[keys[i]].length <= 255) { if (json[keys[i]].length <= 255) {
Object.assign(column, { Object.assign(column, {
dt: 'varchar' dt: 'varchar',
}); });
} else { } else {
Object.assign(column, { Object.assign(column, {
dt: 'text' dt: 'text',
}); });
} }
break; break;
case 'boolean': case 'boolean':
Object.assign(column, { Object.assign(column, {
dt: 'integer' dt: 'integer',
}); });
break; break;
case 'object': case 'object':
Object.assign(column, { Object.assign(column, {
dt: 'text', dt: 'text',
np: null, np: null,
dtxp: null dtxp: null,
}); });
break; break;
default: default:
@ -807,7 +809,7 @@ export class SqliteUi {
colProp.validate = { colProp.validate = {
func: ['isMobilePhone'], func: ['isMobilePhone'],
args: [''], args: [''],
msg: ['Validation failed : isMobilePhone'] msg: ['Validation failed : isMobilePhone'],
}; };
break; break;
case 'Email': case 'Email':
@ -815,7 +817,7 @@ export class SqliteUi {
colProp.validate = { colProp.validate = {
func: ['isEmail'], func: ['isEmail'],
args: [''], args: [''],
msg: ['Validation failed : isEmail'] msg: ['Validation failed : isEmail'],
}; };
break; break;
case 'URL': case 'URL':
@ -823,7 +825,7 @@ export class SqliteUi {
colProp.validate = { colProp.validate = {
func: ['isURL'], func: ['isURL'],
args: [''], args: [''],
msg: ['Validation failed : isURL'] msg: ['Validation failed : isURL'],
}; };
break; break;
case 'Number': case 'Number':
@ -837,7 +839,7 @@ export class SqliteUi {
colProp.validate = { colProp.validate = {
func: ['isCurrency'], func: ['isCurrency'],
args: [''], args: [''],
msg: ['Validation failed : isCurrency'] msg: ['Validation failed : isCurrency'],
}; };
break; break;
case 'Percent': case 'Percent':
@ -847,7 +849,7 @@ export class SqliteUi {
colProp.dt = 'integer'; colProp.dt = 'integer';
break; break;
case 'Rating': case 'Rating':
colProp.dt = 'float'; colProp.dt = 'integer';
break; break;
case 'Formula': case 'Formula':
colProp.dt = 'varchar'; colProp.dt = 'varchar';
@ -911,7 +913,7 @@ export class SqliteUi {
'bigint', 'bigint',
'int2', 'int2',
'int8', 'int8',
'boolean' 'boolean',
]; ];
case 'MultiSelect': case 'MultiSelect':
@ -929,7 +931,7 @@ export class SqliteUi {
'mediumint', 'mediumint',
'bigint', 'bigint',
'int2', 'int2',
'int8' 'int8',
]; ];
case 'Time': case 'Time':
@ -941,7 +943,7 @@ export class SqliteUi {
'mediumint', 'mediumint',
'bigint', 'bigint',
'int2', 'int2',
'int8' 'int8',
]; ];
case 'PhoneNumber': case 'PhoneNumber':
@ -965,7 +967,7 @@ export class SqliteUi {
'real', 'real',
'double', 'double',
'double precision', 'double precision',
'float' 'float',
]; ];
case 'Decimal': case 'Decimal':
@ -985,7 +987,7 @@ export class SqliteUi {
'bigint', 'bigint',
'int2', 'int2',
'int8', 'int8',
'numeric' 'numeric',
]; ];
case 'Percent': case 'Percent':
@ -1002,7 +1004,7 @@ export class SqliteUi {
'bigint', 'bigint',
'int2', 'int2',
'int8', 'int8',
'numeric' 'numeric',
]; ];
case 'Duration': case 'Duration':
@ -1014,15 +1016,11 @@ export class SqliteUi {
'mediumint', 'mediumint',
'bigint', 'bigint',
'int2', 'int2',
'int8' 'int8',
]; ];
case 'Rating': case 'Rating':
return [ return [
'real',
'double',
'double precision',
'float',
'int', 'int',
'integer', 'integer',
'tinyint', 'tinyint',
@ -1031,7 +1029,11 @@ export class SqliteUi {
'bigint', 'bigint',
'int2', 'int2',
'int8', 'int8',
'numeric' 'numeric',
'real',
'double',
'double precision',
'float',
]; ];
case 'Formula': case 'Formula':
@ -1049,7 +1051,7 @@ export class SqliteUi {
'mediumint', 'mediumint',
'bigint', 'bigint',
'int2', 'int2',
'int8' 'int8',
]; ];
case 'Lookup': case 'Lookup':
@ -1072,7 +1074,7 @@ export class SqliteUi {
'mediumint', 'mediumint',
'bigint', 'bigint',
'int2', 'int2',
'int8' 'int8',
]; ];
case 'Barcode': case 'Barcode':

3
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSqlv2.ts

@ -1776,6 +1776,9 @@ class BaseModelSqlv2 {
// let cols = Object.keys(this.columns); // let cols = Object.keys(this.columns);
for (let i = 0; i < this.model.columns.length; ++i) { for (let i = 0; i < this.model.columns.length; ++i) {
const column = this.model.columns[i]; const column = this.model.columns[i];
// skip validation if `validate` is undefined or false
if (!column?.meta?.validate) continue;
const validate = column.getValidators(); const validate = column.getValidators();
const cn = column.column_name; const cn = column.column_name;
if (!validate) continue; if (!validate) continue;

40
packages/nocodb/src/lib/noco-models/Column.ts

@ -56,6 +56,7 @@ export default class Column<T = any> implements ColumnType {
public order: number; public order: number;
public validate: any; public validate: any;
public meta: any;
constructor(data: Partial<ColumnType | Column>) { constructor(data: Partial<ColumnType | Column>) {
Object.assign(this, data); Object.assign(this, data);
@ -106,7 +107,11 @@ export default class Column<T = any> implements ColumnType {
order: column.order, order: column.order,
project_id: column.project_id, project_id: column.project_id,
base_id: column.base_id, base_id: column.base_id,
system: column.system system: column.system,
meta:
column.meta && typeof column.meta === 'object'
? JSON.stringify(column.meta)
: column.meta
}; };
if (column.validate) { if (column.validate) {
@ -375,6 +380,17 @@ export default class Column<T = any> implements ColumnType {
order: 'asc' order: 'asc'
} }
}); });
columnsList.forEach(column => {
if (column.meta && typeof column.meta === 'string') {
try {
column.meta = JSON.parse(column.meta);
} catch {
column.meta = {};
}
}
});
await NocoCache.setList(CacheScope.COLUMN, [fk_model_id], columnsList); await NocoCache.setList(CacheScope.COLUMN, [fk_model_id], columnsList);
} }
columnsList.sort( columnsList.sort(
@ -452,6 +468,11 @@ export default class Column<T = any> implements ColumnType {
MetaTable.COLUMNS, MetaTable.COLUMNS,
colId colId
); );
try {
colData.meta = JSON.parse(colData.meta);
} catch {
colData.meta = {};
}
await NocoCache.set(`${CacheScope.COLUMN}:${colId}`, colData); await NocoCache.set(`${CacheScope.COLUMN}:${colId}`, colData);
} }
if (colData) { if (colData) {
@ -783,7 +804,8 @@ export default class Column<T = any> implements ColumnType {
au: column.au, au: column.au,
pv: column.pv, pv: column.pv,
system: column.system, system: column.system,
validate: null validate: null,
meta: column.meta
}; };
if (column.validate) { if (column.validate) {
@ -801,7 +823,19 @@ export default class Column<T = any> implements ColumnType {
await NocoCache.set(key, o); await NocoCache.set(key, o);
} }
// set meta // set meta
await ncMeta.metaUpdate(null, null, MetaTable.COLUMNS, updateObj, colId); await ncMeta.metaUpdate(
null,
null,
MetaTable.COLUMNS,
{
...updateObj,
meta:
updateObj.meta && typeof updateObj.meta === 'object'
? JSON.stringify(updateObj.meta)
: updateObj.meta
},
colId
);
await this.insertColOption(column, colId, ncMeta); await this.insertColOption(column, colId, ncMeta);
} }

6
packages/nocodb/src/lib/noco/common/XcMigrationSourcev2.ts

@ -2,6 +2,7 @@ import * as nc_011 from '../migrationsv2/nc_011';
import * as nc_012_alter_column_data_types from '../migrationsv2/nc_012_alter_column_data_types'; import * as nc_012_alter_column_data_types from '../migrationsv2/nc_012_alter_column_data_types';
import * as nc_013_sync_source from '../migrationsv2/nc_013_sync_source'; import * as nc_013_sync_source from '../migrationsv2/nc_013_sync_source';
import * as nc_014_alter_column_data_types from '../migrationsv2/nc_014_alter_column_data_types'; import * as nc_014_alter_column_data_types from '../migrationsv2/nc_014_alter_column_data_types';
import * as nc_015_add_meta_col_in_column_table from '../migrationsv2/nc_015_add_meta_col_in_column_table';
// Create a custom migration source class // Create a custom migration source class
export default class XcMigrationSourcev2 { export default class XcMigrationSourcev2 {
@ -14,7 +15,8 @@ export default class XcMigrationSourcev2 {
'nc_011', 'nc_011',
'nc_012_alter_column_data_types', 'nc_012_alter_column_data_types',
'nc_013_sync_source', 'nc_013_sync_source',
'nc_014_alter_column_data_types' 'nc_014_alter_column_data_types',
'nc_015_add_meta_col_in_column_table'
]); ]);
} }
@ -32,6 +34,8 @@ export default class XcMigrationSourcev2 {
return nc_013_sync_source; return nc_013_sync_source;
case 'nc_014_alter_column_data_types': case 'nc_014_alter_column_data_types':
return nc_014_alter_column_data_types; return nc_014_alter_column_data_types;
case 'nc_015_add_meta_col_in_column_table':
return nc_015_add_meta_col_in_column_table;
} }
} }
} }

13
packages/nocodb/src/lib/noco/meta/api/sync/helpers/job.ts

@ -95,7 +95,7 @@ export default async (
async function getAtableSchema(sDB) { async function getAtableSchema(sDB) {
const start = Date.now(); const start = Date.now();
if(sDB.shareId.startsWith('exp')) { if (sDB.shareId.startsWith('exp')) {
const template = await FetchAT.readTemplate(sDB.shareId); const template = await FetchAT.readTemplate(sDB.shareId);
await FetchAT.initialize(template.template.exploreApplication.shareId); await FetchAT.initialize(template.template.exploreApplication.shareId);
} else { } else {
@ -160,9 +160,14 @@ export default async (
function nc_sanitizeName(name) { function nc_sanitizeName(name) {
// knex complains use of '?' in field name // knex complains use of '?' in field name
// good to replace all special characters by _ in one go // good to replace all special characters by _ in one go
// https://stackoverflow.com/questions/18862256/how-to-detect-emoji-using-javascript
const regex = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/g;
const col_name = name const col_name = name
.replace(/\?/g, 'QQ') .replace(/\?/g, '_')
.replace('.', '_') .replace('.', '_')
.replace(regex, '_')
.trim(); .trim();
return col_name; return col_name;
@ -1980,7 +1985,9 @@ export default async (
progress(`Linked data to ${ncTbl.title}`); progress(`Linked data to ${ncTbl.title}`);
} }
} catch (error) { } catch (error) {
progress(`There was an error while migrating data! Please make sure your API key (${syncDB.apiKey}) is correct.`); progress(
`There was an error while migrating data! Please make sure your API key (${syncDB.apiKey}) is correct.`
);
progress(`Error: ${error}`); progress(`Error: ${error}`);
} }
} }

38
packages/nocodb/src/lib/noco/migrationsv2/nc_015_add_meta_col_in_column_table.ts

@ -0,0 +1,38 @@
import Knex from 'knex';
import { MetaTable } from '../../utils/globals';
const up = async (knex: Knex) => {
await knex.schema.alterTable(MetaTable.COLUMNS, table => {
table.text('meta');
});
};
const down = async knex => {
await knex.schema.alterTable(MetaTable.COLUMNS, table => {
table.dropColumn('meta');
});
};
export { up, down };
/**
* @copyright Copyright (c) 2022, Xgene Cloud Ltd
*
* @author Wing-Kam Wong <wingkwong.code@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

4
packages/nocodb/src/lib/noco/upgrader/jobs/ncProjectUpgraderV2_0090000.ts

@ -436,6 +436,10 @@ async function migrateProjectModels(
columnMeta.uidt = UITypes.ForeignKey; columnMeta.uidt = UITypes.ForeignKey;
} }
if (columnMeta.uidt === UITypes.Rating) {
columnMeta.uidt = UITypes.Number;
}
const column = await Column.insert( const column = await Column.insert(
{ {
...columnMeta, ...columnMeta,

10
scripts/markdown/readme/languages/chinese.md

@ -87,22 +87,22 @@ npm start
![1](https://user-images.githubusercontent.com/86527202/136066713-5408634f-5469-40eb-94c9-7eafae5e179c.png) ![1](https://user-images.githubusercontent.com/86527202/136066713-5408634f-5469-40eb-94c9-7eafae5e179c.png)
<br> <br>
![2](https://user-images.githubusercontent.com/86527202/136066729-9b6a261a-231d-4d7f-9fc2-061c301d6192.png) ![2](https://user-images.githubusercontent.com/86527202/168545293-b48a4237-8646-4f9a-a56b-56d5c55adc79.jpg)
<br> <br>
![5](https://user-images.githubusercontent.com/86527202/136066734-3f25aecc-bb7e-4db7-81c4-00b368d799d3.png) ![5](https://user-images.githubusercontent.com/86527202/136066734-3f25aecc-bb7e-4db7-81c4-00b368d799d3.png)
<br> <br>
![6](https://user-images.githubusercontent.com/86527202/136066735-2d4cb656-02dc-4233-ac4a-1ba9bd8acdf0.png) ![6](https://user-images.githubusercontent.com/86527202/168545527-3948125d-1640-4c07-ac80-15db9a85f66f.jpg)
<br> <br>
![7](https://user-images.githubusercontent.com/86527202/136066737-eb6a56fb-5e2e-4423-912b-ced32e8b479c.png) ![7](https://user-images.githubusercontent.com/86527202/168545772-dfbffe13-bcf6-4a49-8a10-1bc8a933d77e.jpg)
<br> <br>
![8](https://user-images.githubusercontent.com/86527202/136066742-94c7eff7-d88e-4002-ad72-ffd23090847c.png) ![8](https://user-images.githubusercontent.com/86527202/168545839-0ba950a4-400f-45b2-b2db-b22b1853af4a.jpg)
<br> <br>
![9](https://user-images.githubusercontent.com/86527202/136066743-1b4030c5-042f-4338-99b0-06237878ce53.png) ![9](https://user-images.githubusercontent.com/86527202/168545872-c2a4b63a-9dc9-4c12-add7-69d5f4d0a6e1.jpg)
<br> <br>
![9a](https://user-images.githubusercontent.com/86527202/136066745-9797775d-7db0-4681-ab10-d7ecbbd972ef.png) ![9a](https://user-images.githubusercontent.com/86527202/136066745-9797775d-7db0-4681-ab10-d7ecbbd972ef.png)

Loading…
Cancel
Save