Browse Source

Merge branch 'develop' into lang-zh_TW

pull/4428/head
Shao Yu-Lung (Allen) 2 years ago committed by GitHub
parent
commit
ac1c602219
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      packages/nc-gui/components.d.ts
  2. 4
      packages/nc-gui/components/cell/Email.vue
  3. 1
      packages/nc-gui/components/dashboard/TreeView.vue
  4. 4
      packages/nc-gui/components/smartsheet/ApiSnippet.vue
  5. 125
      packages/nc-gui/components/smartsheet/Cell.vue
  6. 26
      packages/nc-gui/components/smartsheet/Grid.vue
  7. 34
      packages/nc-gui/components/smartsheet/VirtualCell.vue
  8. 132
      packages/nc-gui/components/smartsheet/header/CellIcon.ts
  9. 89
      packages/nc-gui/components/smartsheet/header/CellIcon.vue
  10. 27
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  11. 75
      packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts
  12. 134
      packages/nc-gui/components/tabs/auth/UserManagement.vue
  13. 4
      packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue
  14. 56
      packages/nc-gui/components/virtual-cell/Lookup.vue
  15. 98
      packages/nc-gui/composables/useColumn.ts
  16. 5
      packages/nc-gui/composables/useProject.ts
  17. 25
      packages/nc-gui/composables/useSmartsheetRowStore.ts
  18. 2
      packages/nc-gui/composables/useTabs.ts
  19. 36
      packages/nc-gui/composables/useVirtualCell.ts
  20. 24
      packages/nc-gui/lang/ar.json
  21. 24
      packages/nc-gui/lang/bn_IN.json
  22. 24
      packages/nc-gui/lang/da.json
  23. 24
      packages/nc-gui/lang/de.json
  24. 24
      packages/nc-gui/lang/es.json
  25. 24
      packages/nc-gui/lang/fa.json
  26. 24
      packages/nc-gui/lang/fi.json
  27. 24
      packages/nc-gui/lang/fr.json
  28. 24
      packages/nc-gui/lang/he.json
  29. 24
      packages/nc-gui/lang/hi.json
  30. 24
      packages/nc-gui/lang/hr.json
  31. 24
      packages/nc-gui/lang/id.json
  32. 24
      packages/nc-gui/lang/it.json
  33. 24
      packages/nc-gui/lang/ja.json
  34. 24
      packages/nc-gui/lang/ko.json
  35. 24
      packages/nc-gui/lang/lv.json
  36. 24
      packages/nc-gui/lang/nl.json
  37. 24
      packages/nc-gui/lang/no.json
  38. 24
      packages/nc-gui/lang/pl.json
  39. 24
      packages/nc-gui/lang/pt.json
  40. 24
      packages/nc-gui/lang/pt_BR.json
  41. 24
      packages/nc-gui/lang/ru.json
  42. 24
      packages/nc-gui/lang/sl.json
  43. 24
      packages/nc-gui/lang/sv.json
  44. 24
      packages/nc-gui/lang/th.json
  45. 24
      packages/nc-gui/lang/tr.json
  46. 24
      packages/nc-gui/lang/uk.json
  47. 24
      packages/nc-gui/lang/vi.json
  48. 2
      packages/nc-gui/layouts/default.vue
  49. 2
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  50. 4
      packages/nc-gui/pages/forgot-password.vue
  51. 4
      packages/nc-gui/pages/signin.vue
  52. 15
      packages/nc-gui/pages/signup/[[token]].vue
  53. 53
      packages/nc-gui/utils/cell.ts
  54. 4
      packages/nc-gui/utils/parsers/parserHelpers.ts
  55. 2
      packages/nc-gui/utils/userUtils.ts
  56. 2
      packages/nc-gui/utils/validation.ts
  57. 19
      packages/nc-gui/utils/virtualCell.ts
  58. 1
      packages/nocodb-sdk/src/index.ts
  59. 0
      packages/nocodb-sdk/src/lib/enums.ts
  60. 2
      packages/nocodb/Dockerfile
  61. 1395
      packages/nocodb/package-lock.json
  62. 2
      packages/nocodb/package.json
  63. 13
      packages/nocodb/src/lib/cache/RedisCacheMgr.ts
  64. 7
      packages/nocodb/src/lib/cache/RedisMockCacheMgr.ts
  65. 374
      packages/nocodb/src/lib/db/sql-client/lib/KnexClient.ts
  66. 2
      packages/nocodb/src/lib/meta/api/apiTokenApis.ts
  67. 2
      packages/nocodb/src/lib/meta/api/ee/orgTokenApis.ts
  68. 2
      packages/nocodb/src/lib/meta/api/orgLicenseApis.ts
  69. 2
      packages/nocodb/src/lib/meta/api/orgTokenApis.ts
  70. 11
      packages/nocodb/src/lib/meta/api/orgUserApis.ts
  71. 11
      packages/nocodb/src/lib/meta/api/projectApis.ts
  72. 2
      packages/nocodb/src/lib/meta/api/projectUserApis.ts
  73. 2
      packages/nocodb/src/lib/meta/api/userApi/initStrategies.ts
  74. 2
      packages/nocodb/src/lib/meta/api/userApi/userApis.ts
  75. 2
      packages/nocodb/src/lib/meta/helpers/ncMetaAclMw.ts
  76. 4
      packages/nocodb/src/lib/models/Project.ts
  77. 65
      packages/nocodb/src/lib/models/ProjectUser.ts
  78. 1
      packages/nocodb/src/lib/utils/globals.ts
  79. 2
      packages/nocodb/src/lib/utils/projectAcl.ts
  80. 2
      packages/nocodb/src/lib/version-upgrader/ncProjectRolesUpgrader.ts
  81. 2
      packages/nocodb/tests/unit/rest/tests/org.test.ts

6
packages/nc-gui/components.d.ts vendored

@ -95,6 +95,8 @@ declare module '@vue/runtime-core' {
MaterialSymbolsDarkModeOutline: typeof import('~icons/material-symbols/dark-mode-outline')['default']
MaterialSymbolsFileCopyOutline: typeof import('~icons/material-symbols/file-copy-outline')['default']
MaterialSymbolsKeyboardReturn: typeof import('~icons/material-symbols/keyboard-return')['default']
MaterialSymbolsKeyboardShift: typeof import('~icons/material-symbols/keyboard-shift')['default']
MaterialSymbolsLightMode: typeof import('~icons/material-symbols/light-mode')['default']
MaterialSymbolsLightModeOutline: typeof import('~icons/material-symbols/light-mode-outline')['default']
MaterialSymbolsRocketLaunchOutline: typeof import('~icons/material-symbols/rocket-launch-outline')['default']
MaterialSymbolsSendOutline: typeof import('~icons/material-symbols/send-outline')['default']
@ -131,7 +133,6 @@ declare module '@vue/runtime-core' {
MdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
MdiChevronLeft: typeof import('~icons/mdi/chevron-left')['default']
MdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
MdiClipboard: typeof import('~icons/mdi/clipboard')['default']
MdiClose: typeof import('~icons/mdi/close')['default']
MdiCloseBox: typeof import('~icons/mdi/close-box')['default']
MdiCloseCircle: typeof import('~icons/mdi/close-circle')['default']
@ -142,7 +143,6 @@ declare module '@vue/runtime-core' {
MdiCommentTextOutline: typeof import('~icons/mdi/comment-text-outline')['default']
MdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
MdiContentSave: typeof import('~icons/mdi/content-save')['default']
MdiCopy: typeof import('~icons/mdi/copy')['default']
MdiCurrencyUsd: typeof import('~icons/mdi/currency-usd')['default']
MdiDatabaseOutline: typeof import('~icons/mdi/database-outline')['default']
MdiDatabaseSync: typeof import('~icons/mdi/database-sync')['default']
@ -180,7 +180,6 @@ declare module '@vue/runtime-core' {
MdiInformation: typeof import('~icons/mdi/information')['default']
MdiJson: typeof import('~icons/mdi/json')['default']
MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
MdiKeyChainVariant: typeof import('~icons/mdi/key-chain-variant')['default']
MdiKeyChange: typeof import('~icons/mdi/key-change')['default']
MdiKeyStar: typeof import('~icons/mdi/key-star')['default']
MdiLink: typeof import('~icons/mdi/link')['default']
@ -207,7 +206,6 @@ declare module '@vue/runtime-core' {
MdiRocketLaunchOutline: typeof import('~icons/mdi/rocket-launch-outline')['default']
MdiScriptTextKeyOutline: typeof import('~icons/mdi/script-text-key-outline')['default']
MdiScriptTextOutline: typeof import('~icons/mdi/script-text-outline')['default']
MdiShieldAccountOutline: typeof import('~icons/mdi/shield-account-outline')['default']
MdiShieldKeyOutline: typeof import('~icons/mdi/shield-key-outline')['default']
MdiSlack: typeof import('~icons/mdi/slack')['default']
MdiSort: typeof import('~icons/mdi/sort')['default']

4
packages/nc-gui/components/cell/Email.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, computed, inject, isEmail, useVModel } from '#imports'
import { EditModeInj, computed, inject, useVModel, validateEmail } from '#imports'
interface Props {
modelValue: string | null | undefined
@ -18,7 +18,7 @@ const editEnabled = inject(EditModeInj)
const vModel = useVModel(props, 'modelValue', emits)
const validEmail = computed(() => vModel.value && isEmail(vModel.value))
const validEmail = computed(() => vModel.value && validateEmail(vModel.value))
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
</script>

1
packages/nc-gui/components/dashboard/TreeView.vue

@ -336,6 +336,7 @@ const onSearchCloseIconClick = () => {
>
<GeneralTooltip class="pl-5 pr-3 py-2" modifier-key="Alt">
<template #title>{{ table.table_name }}</template>
<div class="flex items-center gap-2 h-full" @contextmenu="setMenuContext('table', table)">
<div class="flex w-auto" :data-testid="`tree-view-table-draggable-handle-${table.title}`">
<MdiDragVertical

4
packages/nc-gui/components/smartsheet/ApiSnippet.vue

@ -103,9 +103,9 @@ const activeLang = $computed(() => langs.find((lang) => lang.name === selectedLa
const code = $computed(() => {
if (activeLang?.name === 'nocodb-sdk') {
return `${selectedClient === 'node' ? 'const { Api } require("nocodb-sdk");' : 'import { Api } from "nocodb-sdk";'}
return `${selectedClient === 'node' ? 'const { Api } = require("nocodb-sdk");' : 'import { Api } from "nocodb-sdk";'}
const api = new Api({
baseURL: ${JSON.stringify(apiUrl)},
baseURL: "${(appInfo && appInfo.ncSiteUrl) || '/'}",
headers: {
"xc-auth": ${JSON.stringify(token as string)}
}

125
packages/nc-gui/components/smartsheet/Cell.vue

@ -10,11 +10,36 @@ import {
ReadonlyInj,
computed,
inject,
isAttachment,
isAutoSaved,
isBoolean,
isCurrency,
isDate,
isDateTime,
isDecimal,
isDuration,
isEmail,
isFloat,
isInt,
isJSON,
isManualSaved,
isMultiSelect,
isPercent,
isPhoneNumber,
isPrimary,
isPrimaryKey,
isRating,
isSingleSelect,
isString,
isTextArea,
isTime,
isURL,
isYear,
provide,
ref,
toRef,
useColumn,
useDebounceFn,
useProject,
useSmartsheetRowStoreOrThrow,
useVModel,
} from '#imports'
@ -46,9 +71,7 @@ provide(EditModeInj, useVModel(props, 'editEnabled', emit))
provide(ActiveCellInj, active)
if (readOnly?.value) {
provide(ReadonlyInj, readOnly)
}
provide(ReadonlyInj, readOnly)
const isForm = inject(IsFormInj, ref(false))
@ -58,6 +81,10 @@ const isLocked = inject(IsLockedInj, ref(false))
const { currentRow } = useSmartsheetRowStoreOrThrow()
const { sqlUi } = useProject()
const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value))
const syncValue = useDebounceFn(
() => {
currentRow.value.rowMeta.changed = false
@ -66,33 +93,6 @@ const syncValue = useDebounceFn(
500,
{ maxWait: 2000 },
)
const {
isPrimary,
isURL,
isEmail,
isJSON,
isDate,
isYear,
isDateTime,
isTime,
isBoolean,
isDuration,
isRating,
isCurrency,
isAttachment,
isTextArea,
isString,
isInt,
isFloat,
isDecimal,
isSingleSelect,
isMultiSelect,
isPercent,
isPhoneNumber,
isAutoSaved,
isManualSaved,
isPrimaryKey,
} = useColumn(column)
const vModel = computed({
get: () => props.modelValue,
@ -100,9 +100,9 @@ const vModel = computed({
if (val !== props.modelValue) {
currentRow.value.rowMeta.changed = true
emit('update:modelValue', val)
if (isAutoSaved.value) {
if (isAutoSaved(column.value)) {
syncValue()
} else if (!isManualSaved.value) {
} else if (!isManualSaved(column.value)) {
emit('save')
currentRow.value.rowMeta.changed = true
}
@ -112,7 +112,7 @@ const vModel = computed({
const syncAndNavigate = (dir: NavigateDir, e: KeyboardEvent) => {
console.log('syncAndNavigate', e.target)
if (isJSON.value) return
if (isJSON(column.value)) return
if (currentRow.value.rowMeta.changed || currentRow.value.rowMeta.new) {
emit('save')
@ -127,32 +127,41 @@ const syncAndNavigate = (dir: NavigateDir, e: KeyboardEvent) => {
<template>
<div
class="nc-cell w-full"
:class="[`nc-cell-${(column?.uidt || 'default').toLowerCase()}`, { 'text-blue-600': isPrimary && !virtual && !isForm }]"
:class="[
`nc-cell-${(column?.uidt || 'default').toLowerCase()}`,
{ 'text-blue-600': isPrimary(column) && !virtual && !isForm },
]"
@keydown.enter.exact="syncAndNavigate(NavigateDir.NEXT, $event)"
@keydown.shift.enter.exact="syncAndNavigate(NavigateDir.PREV, $event)"
>
<LazyCellTextArea v-if="isTextArea" v-model="vModel" />
<LazyCellCheckbox v-else-if="isBoolean" v-model="vModel" />
<LazyCellAttachment v-else-if="isAttachment" v-model="vModel" :row-index="props.rowIndex" />
<LazyCellSingleSelect v-else-if="isSingleSelect" v-model="vModel" :row-index="props.rowIndex" />
<LazyCellMultiSelect v-else-if="isMultiSelect" v-model="vModel" :row-index="props.rowIndex" />
<LazyCellDatePicker v-else-if="isDate" v-model="vModel" :is-pk="isPrimaryKey" />
<LazyCellYearPicker v-else-if="isYear" v-model="vModel" :is-pk="isPrimaryKey" />
<LazyCellDateTimePicker v-else-if="isDateTime" v-model="vModel" :is-pk="isPrimaryKey" />
<LazyCellTimePicker v-else-if="isTime" v-model="vModel" :is-pk="isPrimaryKey" />
<LazyCellRating v-else-if="isRating" v-model="vModel" />
<LazyCellDuration v-else-if="isDuration" v-model="vModel" />
<LazyCellEmail v-else-if="isEmail" v-model="vModel" />
<LazyCellUrl v-else-if="isURL" v-model="vModel" />
<LazyCellPhoneNumber v-else-if="isPhoneNumber" v-model="vModel" />
<LazyCellPercent v-else-if="isPercent" v-model="vModel" />
<LazyCellCurrency v-else-if="isCurrency" v-model="vModel" @save="emit('save')" />
<LazyCellDecimal v-else-if="isDecimal" v-model="vModel" />
<LazyCellInteger v-else-if="isInt" v-model="vModel" />
<LazyCellFloat v-else-if="isFloat" v-model="vModel" />
<LazyCellText v-else-if="isString" v-model="vModel" />
<LazyCellJson v-else-if="isJSON" v-model="vModel" />
<LazyCellText v-else v-model="vModel" />
<div v-if="(isLocked || (isPublic && readOnly && !isForm)) && !isAttachment" class="nc-locked-overlay" @click.stop.prevent />
<template v-if="column">
<LazyCellTextArea v-if="isTextArea(column)" v-model="vModel" />
<LazyCellCheckbox v-else-if="isBoolean(column)" v-model="vModel" />
<LazyCellAttachment v-else-if="isAttachment(column)" v-model="vModel" :row-index="props.rowIndex" />
<LazyCellSingleSelect v-else-if="isSingleSelect(column)" v-model="vModel" :row-index="props.rowIndex" />
<LazyCellMultiSelect v-else-if="isMultiSelect(column)" v-model="vModel" :row-index="props.rowIndex" />
<LazyCellDatePicker v-else-if="isDate(column, abstractType)" v-model="vModel" :is-pk="isPrimaryKey(column)" />
<LazyCellYearPicker v-else-if="isYear(column, abstractType)" v-model="vModel" :is-pk="isPrimaryKey(column)" />
<LazyCellDateTimePicker v-else-if="isDateTime(column, abstractType)" v-model="vModel" :is-pk="isPrimaryKey(column)" />
<LazyCellTimePicker v-else-if="isTime(column, abstractType)" v-model="vModel" :is-pk="isPrimaryKey(column)" />
<LazyCellRating v-else-if="isRating(column)" v-model="vModel" />
<LazyCellDuration v-else-if="isDuration(column)" v-model="vModel" />
<LazyCellEmail v-else-if="isEmail(column)" v-model="vModel" />
<LazyCellUrl v-else-if="isURL(column)" v-model="vModel" />
<LazyCellPhoneNumber v-else-if="isPhoneNumber(column)" v-model="vModel" />
<LazyCellPercent v-else-if="isPercent(column)" v-model="vModel" />
<LazyCellCurrency v-else-if="isCurrency(column)" v-model="vModel" @save="emit('save')" />
<LazyCellDecimal v-else-if="isDecimal(column)" v-model="vModel" />
<LazyCellInteger v-else-if="isInt(column, abstractType)" v-model="vModel" />
<LazyCellFloat v-else-if="isFloat(column, abstractType)" v-model="vModel" />
<LazyCellText v-else-if="isString(column, abstractType)" v-model="vModel" />
<LazyCellJson v-else-if="isJSON(column)" v-model="vModel" />
<LazyCellText v-else v-model="vModel" />
<div
v-if="(isLocked || (isPublic && readOnly && !isForm)) && !isAttachment(column)"
class="nc-locked-overlay"
@click.stop.prevent
/>
</template>
</div>
</template>

26
packages/nc-gui/components/smartsheet/Grid.vue

@ -524,19 +524,23 @@ provide(ReloadRowDataHookInj, reloadViewDataHook)
watch(
view,
async (next, old) => {
if (next && next.id !== old?.id) {
// whenever tab changes or view changes save any unsaved data
if (old?.id) {
const oldMeta = await getMeta(old.fk_model_id!)
if (oldMeta) {
await saveOrUpdateRecords({
viewMetaValue: old,
metaValue: oldMeta as TableType,
data: data.value,
})
try {
if (next && next.id !== old?.id) {
// whenever tab changes or view changes save any unsaved data
if (old?.id) {
const oldMeta = await getMeta(old.fk_model_id!)
if (oldMeta) {
await saveOrUpdateRecords({
viewMetaValue: old,
metaValue: oldMeta as TableType,
data: data.value,
})
}
}
await loadData()
}
await loadData()
} catch (e) {
console.log(e)
}
},
{ immediate: true },

34
packages/nc-gui/components/smartsheet/VirtualCell.vue

@ -1,6 +1,22 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import { ActiveCellInj, CellValueInj, ColumnInj, IsFormInj, RowInj, inject, provide, ref, toRef, useVirtualCell } from '#imports'
import {
ActiveCellInj,
CellValueInj,
ColumnInj,
IsFormInj,
RowInj,
inject,
isBt,
isCount,
isFormula,
isHm,
isLookup,
isMm,
isRollup,
provide,
toRef,
} from '#imports'
import type { Row } from '~/lib'
import { NavigateDir } from '~/lib'
@ -24,8 +40,6 @@ provide(CellValueInj, toRef(props, 'modelValue'))
const isForm = inject(IsFormInj, ref(false))
const { isLookup, isBt, isRollup, isMm, isHm, isFormula, isCount } = useVirtualCell(column)
function onNavigate(dir: NavigateDir, e: KeyboardEvent) {
emit('navigate', dir)
@ -39,12 +53,12 @@ function onNavigate(dir: NavigateDir, e: KeyboardEvent) {
@keydown.enter.exact="onNavigate(NavigateDir.NEXT, $event)"
@keydown.shift.enter.exact="onNavigate(NavigateDir.PREV, $event)"
>
<LazyVirtualCellHasMany v-if="isHm" />
<LazyVirtualCellManyToMany v-else-if="isMm" />
<LazyVirtualCellBelongsTo v-else-if="isBt" />
<LazyVirtualCellRollup v-else-if="isRollup" />
<LazyVirtualCellFormula v-else-if="isFormula" />
<LazyVirtualCellCount v-else-if="isCount" />
<LazyVirtualCellLookup v-else-if="isLookup" />
<LazyVirtualCellHasMany v-if="isHm(column)" />
<LazyVirtualCellManyToMany v-else-if="isMm(column)" />
<LazyVirtualCellBelongsTo v-else-if="isBt(column)" />
<LazyVirtualCellRollup v-else-if="isRollup(column)" />
<LazyVirtualCellFormula v-else-if="isFormula(column)" />
<LazyVirtualCellCount v-else-if="isCount(column)" />
<LazyVirtualCellLookup v-else-if="isLookup(column)" />
</div>
</template>

132
packages/nc-gui/components/smartsheet/header/CellIcon.ts

@ -0,0 +1,132 @@
import type { ColumnType } from 'nocodb-sdk'
import type { PropType } from '@vue/runtime-core'
import {
ColumnInj,
computed,
defineComponent,
h,
inject,
isAttachment,
isBoolean,
isCurrency,
isDate,
isDateTime,
isDecimal,
isDuration,
isEmail,
isFloat,
isInt,
isJSON,
isPercent,
isPhoneNumber,
isPrimary,
isRating,
isSet,
isSingleSelect,
isSpecificDBType,
isString,
isTextArea,
isTime,
isURL,
isYear,
toRef,
useProject,
} from '#imports'
import FilePhoneIcon from '~icons/mdi/file-phone'
import KeyIcon from '~icons/mdi/key-variant'
import JSONIcon from '~icons/mdi/code-json'
import ClockIcon from '~icons/mdi/clock-time-five'
import WebIcon from '~icons/mdi/web'
import TextAreaIcon from '~icons/mdi/card-text-outline'
import StringIcon from '~icons/mdi/alpha-a-box-outline'
import BooleanIcon from '~icons/mdi/check-box-outline'
import CalendarIcon from '~icons/mdi/calendar'
import SingleSelectIcon from '~icons/mdi/arrow-down-drop-circle'
import MultiSelectIcon from '~icons/mdi/format-list-bulleted-square'
import DatetimeIcon from '~icons/mdi/calendar-clock'
import RatingIcon from '~icons/mdi/star'
import GenericIcon from '~icons/mdi/square-rounded'
import NumericIcon from '~icons/mdi/numeric'
import AttachmentIcon from '~icons/mdi/image-multiple-outline'
import EmailIcon from '~icons/mdi/email'
import CurrencyIcon from '~icons/mdi/currency-usd-circle-outline'
import PercentIcon from '~icons/mdi/percent-outline'
import DecimalIcon from '~icons/mdi/decimal'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
import DurationIcon from '~icons/mdi/timer-outline'
const renderIcon = (column: ColumnType, abstractType: any) => {
if (isPrimary(column)) {
return KeyIcon
} else if (isJSON(column)) {
return JSONIcon
} else if (isDate(column, abstractType)) {
return CalendarIcon
} else if (isDateTime(column, abstractType)) {
return DatetimeIcon
} else if (isSet(column)) {
return MultiSelectIcon
} else if (isSingleSelect(column)) {
return SingleSelectIcon
} else if (isBoolean(column)) {
return BooleanIcon
} else if (isTextArea(column)) {
return TextAreaIcon
} else if (isEmail(column)) {
return EmailIcon
} else if (isYear(column, abstractType)) {
return CalendarIcon
} else if (isTime(column, abstractType)) {
return ClockIcon
} else if (isRating(column)) {
return RatingIcon
} else if (isAttachment(column)) {
return AttachmentIcon
} else if (isDecimal(column)) {
return DecimalIcon
} else if (isPhoneNumber(column)) {
return FilePhoneIcon
} else if (isURL(column)) {
return WebIcon
} else if (isCurrency(column)) {
return CurrencyIcon
} else if (isDuration(column)) {
return DurationIcon
} else if (isPercent(column)) {
return PercentIcon
} else if (isInt(column, abstractType) || isFloat(column, abstractType)) {
return NumericIcon
} else if (isString(column, abstractType)) {
return StringIcon
} else if (isSpecificDBType(column)) {
return SpecificDBTypeIcon
} else {
return GenericIcon
}
}
export default defineComponent({
name: 'CellIcon',
props: {
columnMeta: {
type: Object as PropType<ColumnType>,
required: false,
},
},
setup(props) {
const columnMeta = toRef(props, 'columnMeta')
const column = inject(ColumnInj, columnMeta)
const { sqlUi } = useProject()
const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value))
return () => {
if (!column.value) return null
return h(renderIcon(column.value, abstractType.value), { class: 'text-grey mx-1 !text-xs' })
}
},
})

89
packages/nc-gui/components/smartsheet/header/CellIcon.vue

@ -1,89 +0,0 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { ColumnInj, computed, inject, toRef, useColumn } from '#imports'
import FilePhoneIcon from '~icons/mdi/file-phone'
import KeyIcon from '~icons/mdi/key-variant'
import JSONIcon from '~icons/mdi/code-json'
import ClockIcon from '~icons/mdi/clock-time-five'
import WebIcon from '~icons/mdi/web'
import TextAreaIcon from '~icons/mdi/card-text-outline'
import StringIcon from '~icons/mdi/alpha-a-box-outline'
import BooleanIcon from '~icons/mdi/check-box-outline'
import CalendarIcon from '~icons/mdi/calendar'
import SingleSelectIcon from '~icons/mdi/arrow-down-drop-circle'
import MultiSelectIcon from '~icons/mdi/format-list-bulleted-square'
import DatetimeIcon from '~icons/mdi/calendar-clock'
import RatingIcon from '~icons/mdi/star'
import GenericIcon from '~icons/mdi/square-rounded'
import NumericIcon from '~icons/mdi/numeric'
import AttachmentIcon from '~icons/mdi/image-multiple-outline'
import EmailIcon from '~icons/mdi/email'
import CurrencyIcon from '~icons/mdi/currency-usd-circle-outline'
import PercentIcon from '~icons/mdi/percent-outline'
import DecimalIcon from '~icons/mdi/decimal'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
import DurationIcon from '~icons/mdi/timer-outline'
const props = defineProps<{ columnMeta?: ColumnType }>()
const columnMeta = toRef(props, 'columnMeta')
const column = inject(ColumnInj, columnMeta)
const additionalColMeta = useColumn(column as Ref<ColumnType>)
const icon = computed(() => {
if (column?.value?.pk) {
return KeyIcon
} else if (additionalColMeta.isJSON.value) {
return JSONIcon
} else if (additionalColMeta.isDate.value) {
return CalendarIcon
} else if (additionalColMeta.isDateTime.value) {
return DatetimeIcon
} else if (additionalColMeta.isSet.value) {
return MultiSelectIcon
} else if (additionalColMeta.isSingleSelect.value) {
return SingleSelectIcon
} else if (additionalColMeta.isBoolean.value) {
return BooleanIcon
} else if (additionalColMeta.isTextArea.value) {
return TextAreaIcon
} else if (additionalColMeta.isEmail.value) {
return EmailIcon
} else if (additionalColMeta.isYear.value) {
return CalendarIcon
} else if (additionalColMeta.isTime.value) {
return ClockIcon
} else if (additionalColMeta.isRating.value) {
return RatingIcon
} else if (additionalColMeta.isAttachment.value) {
return AttachmentIcon
} else if (additionalColMeta.isDecimal.value) {
return DecimalIcon
} else if (additionalColMeta.isPhoneNumber.value) {
return FilePhoneIcon
} else if (additionalColMeta.isURL.value) {
return WebIcon
} else if (additionalColMeta.isCurrency.value) {
return CurrencyIcon
} else if (additionalColMeta.isDuration.value) {
return DurationIcon
} else if (additionalColMeta.isPercent.value) {
return PercentIcon
} else if (additionalColMeta.isInt.value || additionalColMeta.isFloat.value) {
return NumericIcon
} else if (additionalColMeta.isString.value) {
return StringIcon
} else if (additionalColMeta.isSpecificDBType.value) {
return SpecificDBTypeIcon
} else {
return GenericIcon
}
})
</script>
<template>
<component :is="icon" class="text-grey mx-1 !text-xs" />
</template>

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

@ -7,6 +7,12 @@ import {
MetaInj,
computed,
inject,
isBt,
isFormula,
isHm,
isLookup,
isMm,
isRollup,
isVirtualColRequired,
provide,
ref,
@ -14,7 +20,6 @@ import {
useI18n,
useMetas,
useUIPermission,
useVirtualCell,
} from '#imports'
const props = defineProps<{ column: ColumnType; hideMenu?: boolean; required?: boolean | number }>()
@ -37,14 +42,12 @@ const meta = inject(MetaInj, ref())
const isForm = inject(IsFormInj, ref(false))
const { isLookup, isBt, isRollup, isMm, isHm, isFormula } = useVirtualCell(column)
const colOptions = $computed(() => column.value?.colOptions)
const tableTile = $computed(() => meta?.value?.title)
const relationColumnOptions = $computed<LinkToAnotherRecordType | null>(() => {
if (isMm.value || isHm.value || isBt.value) {
if (isMm(column.value) || isHm(column.value) || isBt(column.value)) {
return column.value?.colOptions as LinkToAnotherRecordType
} else if ((column?.value?.colOptions as LookupType | RollupType)?.fk_relation_column_id) {
return meta?.value?.columns?.find(
@ -62,10 +65,10 @@ const relatedTableTitle = $computed(() => relatedTableMeta?.title)
const childColumn = $computed(() => {
if (relatedTableMeta?.columns) {
if (isRollup.value) {
if (isRollup(column.value)) {
return relatedTableMeta?.columns.find((c: ColumnType) => c.id === (colOptions as RollupType).fk_rollup_column_id)
}
if (isLookup.value) {
if (isLookup(column.value)) {
return relatedTableMeta?.columns.find((c: ColumnType) => c.id === (colOptions as LookupType).fk_lookup_column_id)
}
}
@ -76,22 +79,22 @@ const tooltipMsg = computed(() => {
if (!column.value) {
return ''
}
if (isHm.value) {
if (isHm(column.value)) {
return `'${tableTile}' ${t('labels.hasMany')} '${relatedTableTitle}'`
} else if (isMm.value) {
} else if (isMm(column.value)) {
return `'${tableTile}' & '${relatedTableTitle}' ${t('labels.manyToMany')}`
} else if (isBt.value) {
} else if (isBt(column.value)) {
return `'${column?.value?.title}' ${t('labels.belongsTo')} '${relatedTableTitle}'`
} else if (isLookup.value) {
} else if (isLookup(column.value)) {
return `'${childColumn.title}' from '${relatedTableTitle}' (${childColumn.uidt})`
} else if (isFormula.value) {
} else if (isFormula(column.value)) {
const formula = substituteColumnIdWithAliasInFormula(
(column.value?.colOptions as FormulaType)?.formula,
meta?.value?.columns as ColumnType[],
(column.value?.colOptions as any)?.formula_raw,
)
return `Formula - ${formula}`
} else if (isRollup.value) {
} else if (isRollup(column.value)) {
return `'${childColumn.title}' of '${relatedTableTitle}' (${childColumn.uidt})`
}
return ''

75
packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue → packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts

@ -1,8 +1,8 @@
<script setup lang="ts">
import type { PropType } from '@vue/runtime-core'
import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { ColumnInj, inject, ref, toRef } from '#imports'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import { ColumnInj, MetaInj, defineComponent, h, inject, isBt, isHm, isLookup, isMm, isRollup, ref, toRef } from '#imports'
import GenericIcon from '~icons/mdi/square-rounded'
import HMIcon from '~icons/mdi/table-arrow-right'
import BTIcon from '~icons/mdi/table-arrow-left'
@ -13,30 +13,10 @@ import CountIcon from '~icons/mdi/counter'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
import TableColumnPlusBefore from '~icons/mdi/table-column-plus-before'
const props = defineProps<{ columnMeta?: ColumnType }>()
const columnMeta = toRef(props, 'columnMeta')
const column = inject(ColumnInj, ref(columnMeta)) as Ref<ColumnType & { colOptions: LookupType }>
let relationColumn: ColumnType & { colOptions: LookupType }
if (column) {
const { isLookup, isBt, isRollup, isMm, isHm } = useVirtualCell(column as Ref<ColumnType>)
if (isLookup || isBt || isRollup || isMm || isHm) {
const meta = inject(MetaInj, ref())
relationColumn = meta.value?.columns?.find((c) => c.id === column.value?.colOptions?.fk_relation_column_id) as ColumnType & {
colOptions: LinkToAnotherRecordType
}
}
}
const icon = computed(() => {
switch (column?.value?.uidt) {
const renderIcon = (column: ColumnType, relationColumn?: ColumnType) => {
switch (column.uidt) {
case UITypes.LinkToAnotherRecord:
switch ((column?.value?.colOptions as LinkToAnotherRecordType)?.type) {
switch ((column.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: MMIcon, color: 'text-accent' }
case RelationTypes.HAS_MANY:
@ -72,10 +52,43 @@ const icon = computed(() => {
case UITypes.Count:
return { icon: CountIcon, color: 'text-grey' }
}
return { icon: GenericIcon, color: 'text-grey' }
})
</script>
}
export default defineComponent({
name: 'VirtualCellIcon',
props: {
columnMeta: {
type: Object as PropType<ColumnType>,
required: false,
},
},
setup(props) {
const columnMeta = toRef(props, 'columnMeta')
const column = inject(ColumnInj, columnMeta) as Ref<ColumnType & { colOptions: LookupType }>
let relationColumn: ColumnType & { colOptions: LookupType }
return () => {
if (!column.value) return null
<template>
<component :is="icon.icon" class="mx-1 !text-xs" :class="icon.color" />
</template>
if (column && column.value) {
if (isMm(column.value) || isHm(column.value) || isBt(column.value) || isLookup(column.value) || isRollup(column.value)) {
const meta = inject(MetaInj, ref())
relationColumn = meta.value?.columns?.find(
(c) => c.id === column.value?.colOptions?.fk_relation_column_id,
) as ColumnType & {
colOptions: LinkToAnotherRecordType
}
}
}
const { icon: Icon, color } = renderIcon(column.value, relationColumn)
return h(Icon, { class: `${color} mx-1 !text-xs` })
}
},
})

134
packages/nc-gui/components/tabs/auth/UserManagement.vue

@ -1,4 +1,5 @@
<script setup lang="ts">
import { OrgUserRoles } from 'nocodb-sdk'
import type { RequestParams } from 'nocodb-sdk'
import {
extractSdkResponseErrorMsg,
@ -160,6 +161,10 @@ onBeforeMount(async () => {
})
watchDebounced(searchText, () => loadUsers(), { debounce: 300, maxWait: 600 })
const isSuperAdmin = (user: { main_roles?: string }) => {
return user.main_roles?.split(',').includes(OrgUserRoles.SUPER_ADMIN)
}
</script>
<template>
@ -252,6 +257,13 @@ watchDebounced(searchText, () => loadUsers(), { debounce: 300, maxWait: 600 })
</div>
<div class="flex w-1/6 justify-center flex-wrap ml-4">
<div
v-if="isSuperAdmin(user)"
class="rounded-full px-2 py-1 nc-user-role"
:style="{ backgroundColor: projectRoleTagColors[OrgUserRoles.SUPER_ADMIN] }"
>
Super Admin
</div>
<div
v-if="user.roles"
class="rounded-full px-2 py-1 nc-user-role"
@ -261,71 +273,73 @@ watchDebounced(searchText, () => loadUsers(), { debounce: 300, maxWait: 600 })
</div>
</div>
<div class="flex w-1/6 flex-wrap justify-end">
<a-tooltip v-if="user.project_id" placement="bottom">
<template #title>
<span>{{ $t('activity.editUser') }}</span>
</template>
<a-button type="text" class="!rounded-md nc-user-edit" @click="onEdit(user)">
<template #icon>
<IcRoundEdit class="flex mx-auto h-[1rem] text-gray-500" />
<template v-if="!isSuperAdmin(user)">
<a-tooltip v-if="user.project_id" placement="bottom">
<template #title>
<span>{{ $t('activity.editUser') }}</span>
</template>
</a-button>
</a-tooltip>
<!-- Add user to project -->
<a-tooltip v-if="!user.project_id" placement="bottom">
<template #title>
<span>{{ $t('activity.addUserToProject') }}</span>
</template>
<a-button type="text" class="!rounded-md nc-user-invite" @click="inviteUser(user)">
<template #icon>
<MdiPlus class="flex mx-auto h-[1.1rem] text-gray-500" />
<a-button type="text" class="!rounded-md nc-user-edit" @click="onEdit(user)">
<template #icon>
<IcRoundEdit class="flex mx-auto h-[1rem] text-gray-500" />
</template>
</a-button>
</a-tooltip>
<!-- Add user to project -->
<a-tooltip v-if="!user.project_id" placement="bottom">
<template #title>
<span>{{ $t('activity.addUserToProject') }}</span>
</template>
</a-button>
</a-tooltip>
<!-- Remove user from the project -->
<a-tooltip v-else placement="bottom">
<template #title>
<span>{{ $t('activity.deleteUser') }}</span>
</template>
<a-button v-e="['c:user:delete']" type="text" class="!rounded-md nc-user-delete" @click="onDelete(user)">
<template #icon>
<MdiDeleteOutline class="flex mx-auto h-[1.1rem] text-gray-500" />
<a-button type="text" class="!rounded-md nc-user-invite" @click="inviteUser(user)">
<template #icon>
<MdiPlus class="flex mx-auto h-[1.1rem] text-gray-500" />
</template>
</a-button>
</a-tooltip>
<!-- Remove user from the project -->
<a-tooltip v-else placement="bottom">
<template #title>
<span>{{ $t('activity.deleteUser') }}</span>
</template>
</a-button>
</a-tooltip>
<a-dropdown :trigger="['click']" class="flex" placement="bottomRight" overlay-class-name="nc-dropdown-user-mgmt">
<div class="flex flex-row items-center">
<a-button type="text" class="!px-0">
<div class="flex flex-row items-center h-[1.2rem]">
<IcBaselineMoreVert />
</div>
<a-button v-e="['c:user:delete']" type="text" class="!rounded-md nc-user-delete" @click="onDelete(user)">
<template #icon>
<MdiDeleteOutline class="flex mx-auto h-[1.1rem] text-gray-500" />
</template>
</a-button>
</div>
<template #overlay>
<a-menu>
<a-menu-item>
<!-- Resend invite Email -->
<div class="flex flex-row items-center py-3" @click="resendInvite(user)">
<MdiEmailArrowRightOutline class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.resendInvite') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div class="flex flex-row items-center py-3" @click="copyInviteUrl(user)">
<MdiContentCopy class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.copyInviteURL') }}</div>
</a-tooltip>
<a-dropdown :trigger="['click']" class="flex" placement="bottomRight" overlay-class-name="nc-dropdown-user-mgmt">
<div class="flex flex-row items-center">
<a-button type="text" class="!px-0">
<div class="flex flex-row items-center h-[1.2rem]">
<IcBaselineMoreVert />
</div>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-button>
</div>
<template #overlay>
<a-menu>
<a-menu-item>
<!-- Resend invite Email -->
<div class="flex flex-row items-center py-3" @click="resendInvite(user)">
<MdiEmailArrowRightOutline class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.resendInvite') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div class="flex flex-row items-center py-3" @click="copyInviteUrl(user)">
<MdiContentCopy class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.copyInviteURL') }}</div>
</div>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</div>
</div>

4
packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue

@ -3,7 +3,6 @@ import {
Form,
computed,
extractSdkResponseErrorMsg,
isEmail,
message,
onMounted,
projectRoleTagColors,
@ -14,6 +13,7 @@ import {
useI18n,
useNuxtApp,
useProject,
validateEmail,
} from '#imports'
import type { User } from '~/lib'
import { ProjectRole } from '~/lib'
@ -57,7 +57,7 @@ const validators = computed(() => {
callback('Email is required')
return
}
const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !isEmail(e))
const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e))
if (invalidEmails.length > 0) {
callback(`${invalidEmails.length > 1 ? ' Invalid emails:' : 'Invalid email:'} ${invalidEmails.join(', ')} `)
} else {

56
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -1,7 +1,6 @@
<script lang="ts" setup>
import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import type { Ref } from 'vue'
import {
CellUrlDisableOverlayInj,
CellValueInj,
@ -10,43 +9,66 @@ import {
ReadonlyInj,
computed,
inject,
isAttachment,
provide,
refAutoReset,
useColumn,
ref,
useMetas,
watch,
} from '#imports'
const { metas, getMeta } = useMetas()
provide(ReadonlyInj, ref(true))
const column = inject(ColumnInj)! as Ref<ColumnType & { colOptions: LookupType }>
const column = inject(ColumnInj, ref())
const meta = inject(MetaInj, ref())
const value = inject(CellValueInj)
const cellValue = inject(CellValueInj, ref())
const arrValue = computed(() => (Array.isArray(value?.value) ? value?.value : [value?.value]) ?? [])
const arrValue = computed(() => {
if (!cellValue.value) return []
const relationColumn = meta.value?.columns?.find((c) => c.id === column.value.colOptions?.fk_relation_column_id) as ColumnType & {
colOptions: LinkToAnotherRecordType
}
if (Array.isArray(cellValue.value)) return cellValue.value
await getMeta(relationColumn.colOptions.fk_related_model_id!)
return [cellValue.value]
})
const relationColumn = computed(
() =>
meta.value?.columns?.find((c) => c.id === (column.value?.colOptions as LookupType)?.fk_relation_column_id) as
| (ColumnType & {
colOptions: LinkToAnotherRecordType | undefined
})
| undefined,
)
watch(
relationColumn,
async (relationCol) => {
if (relationCol && relationCol.colOptions) await getMeta(relationCol.colOptions.fk_related_model_id!)
},
{ immediate: true },
)
const lookupTableMeta = computed<Record<string, any> | undefined>(() => {
if (relationColumn.value && relationColumn.value?.colOptions)
return metas.value[relationColumn.value.colOptions.fk_related_model_id!]
const lookupTableMeta = computed(() => metas.value[relationColumn.colOptions.fk_related_model_id!])
return undefined
})
const lookupColumn = computed<any>(
const lookupColumn = computed(
() =>
lookupTableMeta.value.columns?.find(
(c: Record<string, any>) => c.id === column.value.colOptions?.fk_lookup_column_id,
) as ColumnType,
lookupTableMeta.value?.columns?.find((c: any) => c.id === (column.value?.colOptions as LookupType)?.fk_lookup_column_id) as
| ColumnType
| undefined,
)
provide(MetaInj, lookupTableMeta)
provide(CellUrlDisableOverlayInj, ref(true))
const lookupColumnMetaProps = useColumn(lookupColumn)
provide(CellUrlDisableOverlayInj, ref(true))
const timeout = 3000 // in ms
@ -94,7 +116,7 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
:key="i"
class="min-w-max"
:class="{
'bg-gray-100 px-1 rounded-full flex-1': !lookupColumnMetaProps.isAttachment,
'bg-gray-100 px-1 rounded-full flex-1': !isAttachment(lookupColumn),
' border-gray-200 rounded border-1': ![UITypes.Attachment, UITypes.MultiSelect, UITypes.SingleSelect].includes(
lookupColumn.uidt,
),

98
packages/nc-gui/composables/useColumn.ts

@ -1,98 +0,0 @@
import type { ColumnType } from 'nocodb-sdk'
import { SqlUiFactory, UITypes, isVirtualCol } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import { computed, useProject } from '#imports'
export function useColumn(column: Ref<ColumnType | undefined>) {
const { project } = useProject()
const uiDatatype: ComputedRef<UITypes> = computed(() => column.value?.uidt as UITypes)
const abstractType = computed(() => {
// kludge: CY test hack; column.value is being received NULL during attach cell delete operation
return (column.value && isVirtualCol(column.value)) || !column.value
? null
: SqlUiFactory.create(
project.value?.bases?.[0]?.type ? { client: project.value.bases[0].type } : { client: 'mysql2' },
).getAbstractType(column.value)
})
const dataTypeLow = computed(() => column.value?.dt?.toLowerCase())
const isBoolean = computed(() => abstractType.value === 'boolean')
const isString = computed(() => uiDatatype.value === UITypes.SingleLineText || abstractType.value === 'string')
const isTextArea = computed(() => uiDatatype.value === UITypes.LongText)
const isInt = computed(() => abstractType.value === 'integer')
const isFloat = computed(() => abstractType.value === 'float' || abstractType.value === UITypes.Number)
const isDate = computed(() => abstractType.value === 'date' || uiDatatype.value === UITypes.Date)
const isYear = computed(() => abstractType.value === 'year' || uiDatatype.value === UITypes.Year)
const isTime = computed(() => abstractType.value === 'time' || uiDatatype.value === UITypes.Time)
const isDateTime = computed(() => abstractType.value === 'datetime' || uiDatatype.value === UITypes.DateTime)
const isJSON = computed(() => uiDatatype.value === UITypes.JSON)
const isEnum = computed(() => uiDatatype.value === UITypes.SingleSelect)
const isSingleSelect = computed(() => uiDatatype.value === UITypes.SingleSelect)
const isSet = computed(() => uiDatatype.value === UITypes.MultiSelect)
const isMultiSelect = computed(() => uiDatatype.value === UITypes.MultiSelect)
const isURL = computed(() => uiDatatype.value === UITypes.URL)
const isEmail = computed(() => uiDatatype.value === UITypes.Email)
const isAttachment = computed(() => uiDatatype.value === UITypes.Attachment)
const isRating = computed(() => uiDatatype.value === UITypes.Rating)
const isCurrency = computed(() => uiDatatype.value === UITypes.Currency)
const isPhoneNumber = computed(() => uiDatatype.value === UITypes.PhoneNumber)
const isDecimal = computed(() => uiDatatype.value === UITypes.Decimal)
const isDuration = computed(() => uiDatatype.value === UITypes.Duration)
const isPercent = computed(() => uiDatatype.value === UITypes.Percent)
const isSpecificDBType = computed(() => uiDatatype.value === UITypes.SpecificDBType)
const isAutoSaved = computed(() =>
[
UITypes.SingleLineText,
UITypes.LongText,
UITypes.PhoneNumber,
UITypes.Email,
UITypes.URL,
UITypes.Number,
UITypes.Decimal,
UITypes.Percent,
UITypes.Count,
UITypes.AutoNumber,
UITypes.SpecificDBType,
UITypes.Geometry,
UITypes.Duration,
].includes(uiDatatype.value),
)
const isManualSaved = computed(() => [UITypes.Currency].includes(uiDatatype.value))
const isPrimary = computed(() => column.value?.pv)
const isPrimaryKey = computed(() => !!column.value?.pk)
return {
abstractType,
dataTypeLow,
isPrimary,
isBoolean,
isString,
isTextArea,
isInt,
isFloat,
isDate,
isYear,
isTime,
isDateTime,
isJSON,
isEnum,
isSet,
isURL,
isEmail,
isAttachment,
isRating,
isCurrency,
isDecimal,
isDuration,
isAutoSaved,
isManualSaved,
isSingleSelect,
isMultiSelect,
isPercent,
isPhoneNumber,
isSpecificDBType,
isPrimaryKey,
}
}

5
packages/nc-gui/composables/useProject.ts

@ -2,6 +2,7 @@ import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk'
import { SqlUiFactory } from 'nocodb-sdk'
import { isString } from '@vueuse/core'
import {
ClientType,
computed,
createEventHook,
ref,
@ -54,13 +55,13 @@ const [setup, use] = useInjectionState(() => {
}
})
const projectBaseType = $computed(() => project.value?.bases?.[0]?.type || '')
const projectBaseType = $computed(() => project.value?.bases?.[0]?.type || ClientType.MYSQL)
const sqlUi = computed(
() => SqlUiFactory.create({ client: projectBaseType }) as Exclude<ReturnType<typeof SqlUiFactory['create']>, typeof OracleUi>,
)
const isMysql = computed(() => ['mysql', 'mysql2'].includes(projectBaseType))
const isMysql = computed(() => ['mysql', ClientType.MYSQL].includes(projectBaseType))
const isMssql = computed(() => projectBaseType === 'mssql')
const isPg = computed(() => projectBaseType === 'pg')
const isSharedBase = computed(() => projectType === 'base')

25
packages/nc-gui/composables/useSmartsheetRowStore.ts

@ -8,6 +8,9 @@ import {
deepCompare,
extractPkFromRow,
extractSdkResponseErrorMsg,
isBt,
isHm,
isMm,
message,
ref,
unref,
@ -16,7 +19,6 @@ import {
useMetas,
useNuxtApp,
useProject,
useVirtualCell,
} from '#imports'
import type { Row } from '~/lib'
@ -36,12 +38,11 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
const state = ref<Record<string, Record<string, any> | Record<string, any>[] | null>>({})
// getters
const isNew = computed(() => unref(row).rowMeta?.new ?? false)
const isNew = computed(() => unref(row).rowMeta.new ?? false)
// actions
const addLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) {
if (isHm(column) || isMm(column)) {
if (!state.value[column.title!]) state.value[column.title!] = []
if (state.value[column.title!]!.find((ln: Record<string, any>) => deepCompare(ln, value))) {
@ -50,17 +51,16 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
}
state.value[column.title!]!.push(value)
} else if (isBt) {
} else if (isBt(column)) {
state.value[column.title!] = value
}
}
// actions
const removeLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) {
if (isHm(column) || isMm(column)) {
state.value[column.title!]?.splice(state.value[column.title!]?.indexOf(value), 1)
} else if (isBt) {
} else if (isBt(column)) {
state.value[column.title!] = null
}
}
@ -92,13 +92,14 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
const id = extractPkFromRow(row, metaValue?.columns as ColumnType[])
for (const column of metaValue?.columns ?? []) {
if (column.uidt !== UITypes.LinkToAnotherRecord) continue
const colOptions = column?.colOptions as LinkToAnotherRecordType
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
const colOptions = column.colOptions as LinkToAnotherRecordType
const relatedTableMeta = metas.value?.[colOptions?.fk_related_model_id as string]
if (isHm || isMm) {
if (isHm(column) || isMm(column)) {
const relatedRows = (state.value?.[column.title!] ?? []) as Record<string, any>[]
for (const relatedRow of relatedRows) {
await linkRecord(
id,
@ -108,7 +109,7 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
{ metaValue },
)
}
} else if (isBt && state?.value?.[column.title!]) {
} else if (isBt(column) && state.value?.[column.title!]) {
await linkRecord(
id,
extractPkFromRow(state.value?.[column.title!] as Record<string, any>, relatedTableMeta.columns as ColumnType[]),

2
packages/nc-gui/composables/useTabs.ts

@ -60,7 +60,7 @@ const [setup, use] = useInjectionState(() => {
if (!tab) return
return navigateToTab(tab)
navigateToTab(tab)
}
},
})

36
packages/nc-gui/composables/useVirtualCell.ts

@ -1,36 +0,0 @@
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { computed } from '#imports'
export function useVirtualCell(column: Ref<ColumnType | undefined>) {
const isHm = computed(
() =>
column.value?.uidt === UITypes.LinkToAnotherRecord &&
(<LinkToAnotherRecordType>column.value?.colOptions).type === RelationTypes.HAS_MANY,
)
const isMm = computed(
() =>
column.value?.uidt === UITypes.LinkToAnotherRecord &&
(<LinkToAnotherRecordType>column.value?.colOptions).type === RelationTypes.MANY_TO_MANY,
)
const isBt = computed(
() =>
column.value?.uidt === UITypes.LinkToAnotherRecord &&
(<LinkToAnotherRecordType>column.value?.colOptions).type === RelationTypes.BELONGS_TO,
)
const isLookup = computed(() => column.value?.uidt === UITypes.Lookup)
const isRollup = computed(() => column.value?.uidt === UITypes.Rollup)
const isFormula = computed(() => column.value?.uidt === UITypes.Formula)
const isCount = computed(() => column.value?.uidt === UITypes.Count)
return {
isHm,
isMm,
isBt,
isLookup,
isRollup,
isFormula,
isCount,
}
}

24
packages/nc-gui/lang/ar.json

@ -104,7 +104,9 @@
"creator": "منشيء",
"editor": "محرر",
"commenter": "معلق",
"viewer": "مشاهد"
"viewer": "مشاهد",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "كتلة برمجية"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "إعلام عبر",
"projName": "اسم المشروع",
"tableName": "اسم الجدول",
@ -296,7 +299,8 @@
"signUpWithGoogle": "التسجيل بواسطة Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "بالتسجيل، أنت توافق على شروط الخدمة",
"welcomeToNc": "مرحبا بكم في NocoDB!"
"welcomeToNc": "مرحبا بكم في NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "إنشاء مشروع",
@ -341,12 +345,14 @@
"invite": "دعوة",
"inviteMore": "دعوة المزيد",
"inviteTeam": "دعوة فريق",
"inviteUser": "Invite User",
"inviteToken": "رمز دعوة",
"newUser": "مستخدم جديد",
"editUser": "تحرير مستخدم",
"deleteUser": "إزالة المستخدم من المشروع",
"resendInvite": "إعادة إرسال دعوة البريد الإلكتروني",
"copyInviteURL": "نسخ رابط الدعوة",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "دور جديد",
"reloadRoles": "إعادة تحميل الأدوار",
"nextPage": "الصفحة التالية",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "الصفوف لكل صفحة",
"upload": "حدد الملف المراد رفعه",
"upload_sub": "أو سحب وإسقاط الملف",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "تصدير البيانات الوصفية للمشروع بنجاح",
@ -683,13 +694,16 @@
"tableDataExported": "تم تصدير جميع بيانات الجدول بنجاح",
"updated": "تم التحديث بنجاح",
"sharedViewDeleted": "تم حذف العرض المشترك بنجاح",
"userDeleted": "User deleted successfully",
"viewRenamed": "تمت إعادة التسمية بنجاح",
"tokenGenerated": "تم إنشاء الرمز بنجاح",
"tokenDeleted": "تم حذف الرمز بنجاح",
"userAddedToProject": "تمت إضافة المستخدم إلى المشروع بنجاح",
"userAdded": "Successfully added user",
"userDeletedFromProject": "تم حذف المستخدم بنجاح من المشروع",
"inviteEmailSent": "دعوة البريد الإلكتروني مرسلة بنجاح",
"inviteURLCopied": "تم نسخ عنوان URL الدعوة إلى الحافظة",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "تم نسخ عنوان URL الأساسي القابل للمشاركة إلى الحافظة!",
"embeddableHTMLCodeCopied": "تم نسخ رمز HTML القابل للدمج!",
"userDetailsUpdated": "تم تحديث تفاصيل المستخدم بنجاح",
@ -699,7 +713,9 @@
"webhookTested": "تم اختبار Webhook بنجاح",
"columnUpdated": "تم تحديث العمود",
"columnCreated": "تم إنشاء العمود",
"passwordChanged": "تم تغيير كلمة المرور بنجاح. الرجاء تسجيل الدخول مرة أخرى."
"passwordChanged": "تم تغيير كلمة المرور بنجاح. الرجاء تسجيل الدخول مرة أخرى.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/bn_IN.json

@ -104,7 +104,9 @@
"creator": "সরষ",
"editor": "সমদক",
"commenter": "মনতবযক",
"viewer": "দরশক"
"viewer": "দরশক",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "এর মযম অবহিত করন",
"projName": "পরকলর নম",
"tableName": "Table name",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "পরকলপ তি করন",
@ -341,12 +345,14 @@
"invite": "আমনরণ",
"inviteMore": "আরও আমনরণ",
"inviteTeam": "দলক আমনরণ করন",
"inviteUser": "Invite User",
"inviteToken": "টনক আমনরণ করন",
"newUser": "নতন বযবহরক",
"editUser": "বযবহরক সমদন করন",
"deleteUser": "পরকলপ থযবহরক সরন",
"resendInvite": "आमरण ईमल द",
"copyInviteURL": "অনিি ইউআরএল আমনরণ করন",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "নতন ভি",
"reloadRoles": "পনরড ভি",
"nextPage": "পরবর",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "পরতিি",
"upload": "আপলড করতইল নিচন করন",
"upload_sub": "অথবইল ট আনন",
@ -654,7 +664,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "পরকলপ ম সফলভ রফতি কর",
@ -684,13 +695,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -700,7 +714,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/da.json

@ -104,7 +104,9 @@
"creator": "Skaber.",
"editor": "Editor.",
"commenter": "Kommenter.",
"viewer": "Viewer."
"viewer": "Viewer.",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Bemærk Via.",
"projName": "Projekt navn",
"tableName": "Tabelnavn.",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Opret projekt",
@ -341,12 +345,14 @@
"invite": "Invitere",
"inviteMore": "Inviter mere",
"inviteTeam": "Inviter Team.",
"inviteUser": "Invite User",
"inviteToken": "Inviter token",
"newUser": "Ny bruger",
"editUser": "Rediger bruger",
"deleteUser": "Fjern bruger fra projektet",
"resendInvite": "Genoplevelse Inviter e-mail på",
"copyInviteURL": "Kopier Inviter URL",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Ny rolle",
"reloadRoles": "Genindlæs roller",
"nextPage": "Næste side",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Rækker per side",
"upload": "Vælg fil for at uploade",
"upload_sub": "eller træk og slip filen",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Project Metadata eksporteres med succes",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/de.json

@ -104,7 +104,9 @@
"creator": "Ersteller",
"editor": "Bearbeiter",
"commenter": "Kommentator",
"viewer": "Zuschauer"
"viewer": "Zuschauer",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL Ansicht"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Ausschnitt"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Benachrichtigen mit",
"projName": "Projektname",
"tableName": "Tabellenname",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "Mit Ihrer Anmeldung stimmen Sie den allgemeinen Nutzungsbedingungen zu",
"welcomeToNc": "Willkommen bei NocoDB!"
"welcomeToNc": "Willkommen bei NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Projekt erstellen",
@ -341,12 +345,14 @@
"invite": "Einladen",
"inviteMore": "Mehr einladen",
"inviteTeam": "Team einladen",
"inviteUser": "Invite User",
"inviteToken": "Token einladen",
"newUser": "Neuer Benutzer",
"editUser": "Benutzer bearbeiten",
"deleteUser": "Benutzer vom Projekt entfernen",
"resendInvite": "Einladungs-Email erneut versenden",
"copyInviteURL": "Einladungs-URL kopieren",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Neue Rolle",
"reloadRoles": "Rollen neu laden",
"nextPage": "Nächste Seite",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Zeilen pro Seite",
"upload": "Datei zum Hochladen auswählen",
"upload_sub": "oder Drag & Drop Datei",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "Die akzeptierten Dateitypen sind .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Projektmetadaten erfolgreich exportiert",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "Ansicht erfolgreich umbenannt",
"tokenGenerated": "Token erfolgreich generiert",
"tokenDeleted": "Token erfolgreich gelöscht",
"userAddedToProject": "Benutzer erfolgreich zum Projekt hinzugefügt",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Benutzer erfolgreich aus dem Projekt gelöscht",
"inviteEmailSent": "Einladungs-E-Mail erfolgreich gesendet",
"inviteURLCopied": "Einladungslink wurde in Zwischenablage kopiert",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Benutzerdaten erfolgreich aktualisiert",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Spalte aktualisiert",
"columnCreated": "Spalte erstellt",
"passwordChanged": "Kennwort erfolgreich geändert. Bitte melden Sie sich erneut an."
"passwordChanged": "Kennwort erfolgreich geändert. Bitte melden Sie sich erneut an.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/es.json

@ -104,7 +104,9 @@
"creator": "Creador",
"editor": "Editor",
"commenter": "Comentarista",
"viewer": "Visor"
"viewer": "Visor",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Notificar a través de",
"projName": "Nombre del proyecto",
"tableName": "Nombre de la tabla",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Crear proyecto",
@ -341,12 +345,14 @@
"invite": "Invitar",
"inviteMore": "Invitar más",
"inviteTeam": "Invitar al equipo",
"inviteUser": "Invite User",
"inviteToken": "Invitar con token",
"newUser": "Nuevo usuario",
"editUser": "Editar usuario",
"deleteUser": "Eliminar usuario del proyecto",
"resendInvite": "Reenviar la invitación al correo electrónico",
"copyInviteURL": "Copiar URL de invitación",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nuevo rol",
"reloadRoles": "Recargar roles",
"nextPage": "Siguiente página",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Filas por página",
"upload": "Selecciona Archivo para cargar",
"upload_sub": "o arrastra y suelta el archivo",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Metadatos del proyecto exportados con éxito.",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/fa.json

@ -104,7 +104,9 @@
"creator": "ایجادکننده",
"editor": "ویرایشگر",
"commenter": "نظردهنده",
"viewer": "بیننده"
"viewer": "بیننده",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "اعلان از طریق",
"projName": "نام پروژه",
"tableName": "نام جدول",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "ایجاد پروژه",
@ -341,12 +345,14 @@
"invite": "دعوت",
"inviteMore": "دعوت از افراد بیشتر",
"inviteTeam": "دعوت از تیم",
"inviteUser": "Invite User",
"inviteToken": "توکن دعوتنامه",
"newUser": "کاربر جدید",
"editUser": "ویرایش کاربر",
"deleteUser": "حذف کاربر از پروژه",
"resendInvite": "ارسال مجدد پست الکترونیکی دعوت",
"copyInviteURL": "URL دعوت را کپی کنید",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "نقش جدید",
"reloadRoles": "بارگذاری مجدد نقشها",
"nextPage": "صفحه بعدی",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "تعداد ردیفها در هر صفحه",
"upload": "فایل را برای بارگذاری انتخاب کنید",
"upload_sub": "یا فایل را بکشید و رها کنید",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "فراداده پروژه با موفقیت خارج شد",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/fi.json

@ -104,7 +104,9 @@
"creator": "Luoja",
"editor": "Toimittaja",
"commenter": "Kommentti",
"viewer": "Katselija"
"viewer": "Katselija",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Ilmoittaa kautta",
"projName": "Projektin nimi",
"tableName": "Taulukon nimi",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Luo projekti",
@ -341,12 +345,14 @@
"invite": "Kutsua",
"inviteMore": "Kutsu lisää",
"inviteTeam": "Kutsu tiimi",
"inviteUser": "Invite User",
"inviteToken": "Kutsua token",
"newUser": "Uusi käyttäjä",
"editUser": "Muokkaa käyttäjää",
"deleteUser": "Poista käyttäjä projektista",
"resendInvite": "Lähetä sähköpostia uudelleen",
"copyInviteURL": "Kopioi kutsua URL-osoite",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Uusi rooli",
"reloadRoles": "Lataa roolit uudelleen",
"nextPage": "Seuraava sivu",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Rivit sivua kohti",
"upload": "Valitse lataa tiedosto",
"upload_sub": "tai vedä ja pudota tiedosto",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Project Metadata viedään onnistuneesti",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/fr.json

@ -104,7 +104,9 @@
"creator": "Créateur",
"editor": "Éditeur",
"commenter": "Commentateur",
"viewer": "Lecture seule"
"viewer": "Lecture seule",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "Vue SQL"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Notifier via",
"projName": "Nom du projet",
"tableName": "Nom de la table",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Créer un projet",
@ -341,12 +345,14 @@
"invite": "Inviter",
"inviteMore": "Inviter plus",
"inviteTeam": "Inviter une équipe",
"inviteUser": "Invite User",
"inviteToken": "Inviter via un jeton",
"newUser": "Nouvel utilisateur",
"editUser": "Modifier l'utilisateur",
"deleteUser": "Supprimer l'utilisateur du projet",
"resendInvite": "Renvoyer une invitation par courriel",
"copyInviteURL": "Copier l'URL d'invitation",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nouveau rôle",
"reloadRoles": "Actualiser les rôles",
"nextPage": "Page suivante",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Lignes par page",
"upload": "Sélectionner un fichier à téléverser",
"upload_sub": "ou glisser-déposer un fichier",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Les métadonnées de projet sont exportées avec succès",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Vue partagée effacée avec succès",
"userDeleted": "User deleted successfully",
"viewRenamed": "Vue renommée avec succès",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/he.json

@ -104,7 +104,9 @@
"creator": "יוצר",
"editor": "עוֹרֵך",
"commenter": "פרשן",
"viewer": "צוֹפֶה"
"viewer": "צוֹפֶה",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "תודיע דרך",
"projName": "שם הפרוייקט",
"tableName": "שם שולחן",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "צור פרויקט",
@ -341,12 +345,14 @@
"invite": "הזמן",
"inviteMore": "הזמן עוד",
"inviteTeam": "הזמן צוות",
"inviteUser": "Invite User",
"inviteToken": "הזמן טוקן",
"newUser": "משתמש חדש",
"editUser": "ערוך משתמש",
"deleteUser": "הסר משתמש מהפרויקט",
"resendInvite": "של הזמנת אימייל בשנית",
"copyInviteURL": "העתק הזמנת URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "תפקיד חדש",
"reloadRoles": "טען מחדש את התפקידים",
"nextPage": "עמוד הבא",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "שורות לדף",
"upload": "בחר קובץ להעלאה",
"upload_sub": "או גרור ושחרר קובץ",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "פרויקט Metadata מיוצא בהצלחה",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/hi.json

@ -104,7 +104,9 @@
"creator": "बन",
"editor": "सदक",
"commenter": "टिपणर",
"viewer": "दरशक"
"viewer": "दरशक",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Notify Via",
"projName": "परिजनम",
"tableName": "Table name",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "पट बन",
@ -341,12 +345,14 @@
"invite": "आमित करन",
"inviteMore": "अधिक आमित कर",
"inviteTeam": "टम क आमित कर",
"inviteUser": "Invite User",
"inviteToken": "टकन क आमित कर",
"newUser": "नय उपयगकर",
"editUser": "यजर कित कर",
"deleteUser": "परिजन उपयगकरि",
"resendInvite": "आमरण ईमल द",
"copyInviteURL": "क आमित URL",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "नयि",
"reloadRoles": "पड भि",
"nextPage": "अगलठ",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "परतिठ पि",
"upload": "अपलड करनिए फइल क चयन कर",
"upload_sub": "यग एड डप फइल",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "परिजन सफलतवक नित क गई",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/hr.json

@ -104,7 +104,9 @@
"creator": "Kreator",
"editor": "Urednik",
"commenter": "Komentator",
"viewer": "Preglednika"
"viewer": "Preglednika",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Obavijestiti putem",
"projName": "Naziv projekta",
"tableName": "Tablica",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Stvoriti projekt",
@ -341,12 +345,14 @@
"invite": "Pozvati",
"inviteMore": "Pozvati više",
"inviteTeam": "Pozvati tim",
"inviteUser": "Invite User",
"inviteToken": "Pozvati token",
"newUser": "Novi korisnik",
"editUser": "Uredi korisnika",
"deleteUser": "Uklonite korisnika od projekta",
"resendInvite": "Ponovno pošaljite e-poštu",
"copyInviteURL": "Kopirajte URL",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nova uloga",
"reloadRoles": "Ponovno učitajte uloge",
"nextPage": "Sljedeća stranica",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Redaka po stranici",
"upload": "Odaberite datoteku za prijenos",
"upload_sub": "ili povucite i ispustite datoteku",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Projektni metapodaci su uspješno izvozili",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/id.json

@ -104,7 +104,9 @@
"creator": "Pencipta",
"editor": "Editor",
"commenter": "Komentator",
"viewer": "Penonton"
"viewer": "Penonton",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Beri tahu VIA.",
"projName": "Nama Proyek",
"tableName": "Nama meja",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Buat Proyek",
@ -341,12 +345,14 @@
"invite": "Undang",
"inviteMore": "Undang lebih banyak",
"inviteTeam": "Undang Tim",
"inviteUser": "Invite User",
"inviteToken": "Undang token.",
"newUser": "Pengguna baru",
"editUser": "Edit Pengguna",
"deleteUser": "Hapus pengguna dari proyek",
"resendInvite": "Kirim ulang undangan e-mail",
"copyInviteURL": "Salin Undangan URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Peran Baru",
"reloadRoles": "Muat ulang peran",
"nextPage": "Halaman selanjutnya",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Baris per halaman",
"upload": "Pilih file untuk diunggah",
"upload_sub": "atau seret dan jatuhkan file",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Metadata proyek berhasil diekspor",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/it.json

@ -104,7 +104,9 @@
"creator": "Creatore",
"editor": "Editor",
"commenter": "Commentatore",
"viewer": "Spettatore"
"viewer": "Spettatore",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Notifica tramite",
"projName": "Nome del progetto",
"tableName": "Nome della tabella",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Crea progetto",
@ -341,12 +345,14 @@
"invite": "Invita",
"inviteMore": "Invita di più",
"inviteTeam": "Invita un team.",
"inviteUser": "Invite User",
"inviteToken": "Invita con una chiave",
"newUser": "Nuovo utente",
"editUser": "Modifica utente",
"deleteUser": "Rimuovi utente dal progetto",
"resendInvite": "Reinvia e-mail di invito",
"copyInviteURL": "Copia l'URL dell'invito",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nuovo ruolo",
"reloadRoles": "Ricarica ruoli",
"nextPage": "Pagina successiva",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Righe per pagina",
"upload": "Seleziona il file da caricare",
"upload_sub": "o trascinalo qui",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Metadati del progetto esportati con successo",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/ja.json

@ -104,7 +104,9 @@
"creator": "作成者",
"editor": "編集者",
"commenter": "コメンター",
"viewer": "閲覧者"
"viewer": "閲覧者",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "次で通知する",
"projName": "プロジェクト名",
"tableName": "テーブルの名前",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "プロジェクトを作成",
@ -341,12 +345,14 @@
"invite": "招待",
"inviteMore": "さらに招待",
"inviteTeam": "チームへ招待",
"inviteUser": "Invite User",
"inviteToken": "招待用トークン",
"newUser": "ユーザーを作成",
"editUser": "ユーザーを編集",
"deleteUser": "プロジェクトからユーザーを削除",
"resendInvite": "招待用メールを再送信",
"copyInviteURL": "招待用 URL をコピー",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "新しいロール",
"reloadRoles": "ロールをリロード",
"nextPage": "次のページ",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "1ページあたりの行数",
"upload": "アップロードするファイルを選択してください",
"upload_sub": "またはファイルをドラッグ & ドロップ",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "プロジェクトメタデータは正常にエクスポートされました",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "列が更新されました",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/ko.json

@ -104,7 +104,9 @@
"creator": "생성자",
"editor": "편집자",
"commenter": "해설자",
"viewer": "뷰어"
"viewer": "뷰어",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "공지",
"projName": "프로젝트 이름",
"tableName": "테이블 이름",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "프로젝트 생성",
@ -341,12 +345,14 @@
"invite": "초대",
"inviteMore": "더 많은 사람 초대",
"inviteTeam": "팀 초대",
"inviteUser": "Invite User",
"inviteToken": "초대 토큰",
"newUser": "사용자 추가",
"editUser": "사용자 편집",
"deleteUser": "사용자 제거",
"resendInvite": "초대 이메일 재전송",
"copyInviteURL": "초대 URL 복사",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "새로운 역할",
"reloadRoles": "역할 다시 불러오기",
"nextPage": "다음 페이지",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "페이지 당 행",
"upload": "업로드 할 파일 선택",
"upload_sub": "또는 끌어서 놓기 파일",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "프로젝트 메타 데이터를 성공적으로 내보냈습니다.",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/lv.json

@ -104,7 +104,9 @@
"creator": "Autors",
"editor": "Redaktors",
"commenter": "Komentētājs",
"viewer": "Skatītājs"
"viewer": "Skatītājs",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Paziņot izmantojot",
"projName": "Projekta nosaukums",
"tableName": "Tabulas nosaukums",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Izveidot projektu",
@ -341,12 +345,14 @@
"invite": "Uzaicināt",
"inviteMore": "Uzaicināt vēl",
"inviteTeam": "Uzaicināt komandu",
"inviteUser": "Invite User",
"inviteToken": "Uzaicināšanas talons",
"newUser": "Jauns lietotājs",
"editUser": "Rediģēt lietotāju",
"deleteUser": "Noņemt lietotāju no projektu",
"resendInvite": "Atkārtoti nosūtīt uzaicinājumu",
"copyInviteURL": "Kopēt uzaicinājuma saiti",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Jauna loma",
"reloadRoles": "Pārlādēt lomas",
"nextPage": "Nākošā lapa",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "ieraksti lapā",
"upload": "Izvēlēties datni augšupielādei",
"upload_sub": "vai vilkt un nomest datni",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Projekta metadati eksportēti veiksmīgi",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/nl.json

@ -104,7 +104,9 @@
"creator": "Creator",
"editor": "Bewerker",
"commenter": "Reviewer",
"viewer": "Kijker"
"viewer": "Kijker",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Melding via",
"projName": "Projectnaam",
"tableName": "Tabelnaam",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Maak project",
@ -341,12 +345,14 @@
"invite": "Uitnodigen",
"inviteMore": "Nodig meer gebruikers uit",
"inviteTeam": "Nodig team uit",
"inviteUser": "Invite User",
"inviteToken": "Uitnodigingstoken",
"newUser": "Nieuwe gebruiker",
"editUser": "Bewerk gebruiker",
"deleteUser": "Verwijder gebruiker van project",
"resendInvite": "Verzend uitnodigingsemail opnieuw",
"copyInviteURL": "Kopieer uitnodigingslink",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nieuwe rol",
"reloadRoles": "Rollen verversen",
"nextPage": "Volgende pagina",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Rijen per pagina",
"upload": "Selecteer bestand om te uploaden",
"upload_sub": "of sleep het bestand naar hier",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Project metadata met succes geëxporteerd",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/no.json

@ -104,7 +104,9 @@
"creator": "Skaperen.",
"editor": "Redaktør",
"commenter": "Kommenterer",
"viewer": "Seer."
"viewer": "Seer.",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Varsle Via.",
"projName": "Prosjektnavn",
"tableName": "Tabellnavn",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Opprett prosjekt",
@ -341,12 +345,14 @@
"invite": "Invitere",
"inviteMore": "Inviter mer",
"inviteTeam": "Invitere team",
"inviteUser": "Invite User",
"inviteToken": "Inviter TOKEN.",
"newUser": "Ny bruker",
"editUser": "Rediger bruker",
"deleteUser": "Fjern brukeren fra prosjektet",
"resendInvite": "Resend invitere e-post",
"copyInviteURL": "Kopier Inviter URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Ny rolle",
"reloadRoles": "Oppdater roller",
"nextPage": "Neste side",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Rader per side.",
"upload": "Velg Fil for å laste opp",
"upload_sub": "eller dra og slipp filen",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Prosjektmetadata eksporteres vellykket",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/pl.json

@ -104,7 +104,9 @@
"creator": "Twórca",
"editor": "Redaktor",
"commenter": "Komentator",
"viewer": "Widz"
"viewer": "Widz",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "Widok SQL"
},
@ -200,6 +202,7 @@
"codeSnippet": "Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Powiadomić VIA.",
"projName": "Nazwa Projektu",
"tableName": "Nazwa tabeli",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Zarejestruj się przez Google",
"signInWithGoogle": "Zaloguj się przez Google",
"agreeToTos": "Rejestrując się, akceptujesz warunki korzystania z usługi",
"welcomeToNc": "Witaj w NocoDB!"
"welcomeToNc": "Witaj w NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Utwórz projekt",
@ -341,12 +345,14 @@
"invite": "Zapraszać",
"inviteMore": "Zaprosić więcej",
"inviteTeam": "Invite Team.",
"inviteUser": "Invite User",
"inviteToken": "Zaproś token.",
"newUser": "Nowy użytkownik",
"editUser": "Edytuj użytkownika",
"deleteUser": "Usuń użytkownika z projektu",
"resendInvite": "Ponownie zaprosić zaproszenie e-maila",
"copyInviteURL": "Kopiuj Zaproś Url.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nowa rola",
"reloadRoles": "Role ponownie załaduj",
"nextPage": "Następna strona",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Wiersze na stronę",
"upload": "Wybierz plik do przesłania",
"upload_sub": "lub przeciągnij i upuść plik",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "Akceptowane typy plików to: .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Klucz parametru nie może być pusty",
"duplicateParameterKeysAreNotAllowed": "Zduplikowane klucze parametrów są niedozwolone",
"fieldRequired": "{value} nie może być puste."
"fieldRequired": "{value} nie może być puste.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Pomyślnie wyeksportowano metadane projektu",
@ -683,13 +694,16 @@
"tableDataExported": "Pomyślnie wyeksportowano wszystkie dane tabeli",
"updated": "Zaktualizowano pomyślnie",
"sharedViewDeleted": "Usunięto udostępniony widok",
"userDeleted": "User deleted successfully",
"viewRenamed": "Zmieniono nazwę widoku",
"tokenGenerated": "Token został wygenerowany pomyślnie",
"tokenDeleted": "Token usunięty pomyślnie",
"userAddedToProject": "Pomyślnie dodano użytkownika do projektu",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Użytkownik usunięty z projektu",
"inviteEmailSent": "E-mail z zaproszeniem wysłany pomyślnie",
"inviteURLCopied": "Adres URL zaproszenia skopiowany do schowka",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Skopiowano adres URL do schowka!",
"embeddableHTMLCodeCopied": "Skopiowany kod HTML do osadzenia!",
"userDetailsUpdated": "Pomyślnie zaktualizowano dane użytkownika",
@ -699,7 +713,9 @@
"webhookTested": "Webhook przetestowany pomyślnie",
"columnUpdated": "Kolumna zaktualizowana",
"columnCreated": "Kolumna utworzona",
"passwordChanged": "Hasło zostało zmienione. Zaloguj się ponownie."
"passwordChanged": "Hasło zostało zmienione. Zaloguj się ponownie.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/pt.json

@ -104,7 +104,9 @@
"creator": "O Criador",
"editor": "editor",
"commenter": "Comentarista",
"viewer": "Viewer."
"viewer": "Viewer.",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Notificar via.",
"projName": "Nome do Projeto",
"tableName": "Nome da tabela",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Criar Projecto",
@ -341,12 +345,14 @@
"invite": "Convidar",
"inviteMore": "Convide mais",
"inviteTeam": "Convidar equipe",
"inviteUser": "Invite User",
"inviteToken": "Convidar Token.",
"newUser": "Novo usuário",
"editUser": "Editar usuário",
"deleteUser": "Remover usuário do projeto",
"resendInvite": "Reenviar o envide e-mail",
"copyInviteURL": "Copiar convidar URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Novo papel",
"reloadRoles": "Recarregar funções",
"nextPage": "Próxima página",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Linhas por página",
"upload": "Selecione Arquivo para upload",
"upload_sub": "ou arrastar e soltar arquivo",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Metadados do projeto exportado com sucesso",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/pt_BR.json

@ -104,7 +104,9 @@
"creator": "O Criador",
"editor": "editor",
"commenter": "Comentarista",
"viewer": "Viewer."
"viewer": "Viewer.",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Notificar via.",
"projName": "Nome do Projeto",
"tableName": "Nome da tabela",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Criar Projeto",
@ -341,12 +345,14 @@
"invite": "Convidar",
"inviteMore": "Convide mais",
"inviteTeam": "Convidar equipe",
"inviteUser": "Invite User",
"inviteToken": "Convidar Token.",
"newUser": "Novo usuário",
"editUser": "Editar usuário",
"deleteUser": "Remover usuário do projeto",
"resendInvite": "Reenviar o envide e-mail",
"copyInviteURL": "Copiar convidar URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Novo papel",
"reloadRoles": "Recarregar funções",
"nextPage": "Próxima página",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Linhas por página",
"upload": "Selecione Arquivo para upload",
"upload_sub": "ou arrastar e soltar arquivo",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Metadados do projeto exportado com sucesso",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/ru.json

@ -104,7 +104,9 @@
"creator": "Создатель",
"editor": "Редактор",
"commenter": "Комментатор",
"viewer": "Просмотр"
"viewer": "Просмотр",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Уведомлять через",
"projName": "Название проекта",
"tableName": "Название таблицы",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Создать проект",
@ -341,12 +345,14 @@
"invite": "Пригласить",
"inviteMore": "Пригласить еще",
"inviteTeam": "Пригласить команду",
"inviteUser": "Invite User",
"inviteToken": "Токен приглашения",
"newUser": "Новый пользователь",
"editUser": "Редактировать пользователя",
"deleteUser": "Удалить пользователя из проекта",
"resendInvite": "Переотправить приглашение e-mail",
"copyInviteURL": "Скопировать URL-адрес приглашения",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Новая роль",
"reloadRoles": "Перезагрузить роли",
"nextPage": "Следущая страница",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Строк на страницу",
"upload": "Выберите файл для загрузки",
"upload_sub": "или перетащите файл",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Метаданные проекта успешно экспортированы",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/sl.json

@ -104,7 +104,9 @@
"creator": "Ustvarjalec",
"editor": "Urednik",
"commenter": "Komentar",
"viewer": "Gledalca."
"viewer": "Gledalca.",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Obvestite VIO.",
"projName": "Ime Projekta",
"tableName": "Ime tabele",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Ustvari projek",
@ -341,12 +345,14 @@
"invite": "Povabi",
"inviteMore": "Povabite več.",
"inviteTeam": "Povabite ekipo",
"inviteUser": "Invite User",
"inviteToken": "Povabite žeton",
"newUser": "Nov uporabnik",
"editUser": "Uredite uporabnika",
"deleteUser": "Odstrani uporabnika iz projekta",
"resendInvite": "Ponovno pošljite e-pošto",
"copyInviteURL": "Kopiraj Povabi URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Nova vloga",
"reloadRoles": "Ponovno naloži vloge",
"nextPage": "Naslednja stran",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Vrstice na stran.",
"upload": "Izberite datoteko za nalaganje",
"upload_sub": "ali datoteko povlecite in spustite",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Projekt Metapodatki se je uspešno izvozil",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/sv.json

@ -104,7 +104,9 @@
"creator": "Skapare",
"editor": "Redaktör",
"commenter": "Kommentare",
"viewer": "Visare"
"viewer": "Visare",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Meddela via",
"projName": "Projektnamn",
"tableName": "Tabellnamn",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Skapa projekt",
@ -341,12 +345,14 @@
"invite": "Inbjudan",
"inviteMore": "Bjud in mer",
"inviteTeam": "Bjud in lag",
"inviteUser": "Invite User",
"inviteToken": "Bjud in token",
"newUser": "Ny användare",
"editUser": "Redigera användaren",
"deleteUser": "Ta bort användaren från projektet",
"resendInvite": "Ändra bjud in e-post",
"copyInviteURL": "Kopiera inbjudan url",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Ny roll",
"reloadRoles": "Ladda om roller",
"nextPage": "Nästa sida",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Rader per sida",
"upload": "Välj fil för att ladda upp",
"upload_sub": "eller dra och släpp filen",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Projektmetadata exporterades framgångsrikt",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/th.json

@ -104,7 +104,9 @@
"creator": "ผสราง",
"editor": "บรรณาธการ",
"commenter": "ผจารณ",
"viewer": "ผ"
"viewer": "ผ",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "แจงเตอนผาน",
"projName": "ชอโครงการ",
"tableName": "ชอตาราง",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "สรางโครงการ",
@ -341,12 +345,14 @@
"invite": "เชญชวน",
"inviteMore": "เชญมากขน",
"inviteTeam": "เชญทม",
"inviteUser": "Invite User",
"inviteToken": "เชญโทเคน",
"newUser": "ผใชใหม",
"editUser": "แกไขผใช",
"deleteUser": "ลบผใชจากโครงการ",
"resendInvite": "สงอเมลเชญสงอเมลอกครง",
"copyInviteURL": "Copy Invite URL",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "บทบาทใหม",
"reloadRoles": "โหลดบทบาทใหม",
"nextPage": "หนาตอไป",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "แถวตอหนา",
"upload": "เลอกไฟลจะอปโหลด",
"upload_sub": "หรอลากและวางไฟล",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "ขอมลเมตาของโครงการสงออกเรยบรอยแลว",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/tr.json

@ -104,7 +104,9 @@
"creator": "Yaratıcı",
"editor": "Editör",
"commenter": "Yorumcu",
"viewer": "İzleyici"
"viewer": "İzleyici",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Aracılığıyla Bildir",
"projName": "Proje adı",
"tableName": "Tablo adı",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Proje Oluştur",
@ -341,12 +345,14 @@
"invite": "Davet et",
"inviteMore": "Daha fazla davet et",
"inviteTeam": "Takıma davet et",
"inviteUser": "Invite User",
"inviteToken": "Davet kodu",
"newUser": "Yeni kullanıcı",
"editUser": "Kullanıcıyı düzenle",
"deleteUser": "Kullanıcıyı projeden kaldır",
"resendInvite": "Davet e-postasını yeniden gönder",
"copyInviteURL": "Davet linkini kopyala",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Yeni rol",
"reloadRoles": "Rolleri yeniden yükle",
"nextPage": "Sonraki Sayfa",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Sayfa başına satır",
"upload": "Yüklenecek Dosyayı Seçin",
"upload_sub": "veya dosyayı sürükleyip bırakın",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Proje metaverileri başarıyla dışa aktarıldı",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/uk.json

@ -104,7 +104,9 @@
"creator": "Творець",
"editor": "Редактор",
"commenter": "Коментатор",
"viewer": "Глядач"
"viewer": "Глядач",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Повідомити через",
"projName": "Назва проекту",
"tableName": "Назва таблиці",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Створити проект",
@ -341,12 +345,14 @@
"invite": "Запрошувати",
"inviteMore": "Запросити більше",
"inviteTeam": "Запрошувати команду",
"inviteUser": "Invite User",
"inviteToken": "Запросити токен",
"newUser": "Новий користувач",
"editUser": "Редагувати користувача",
"deleteUser": "Видалити користувача з проекту",
"resendInvite": "Перевести запрошення електронної пошти",
"copyInviteURL": "Копіювати запросити URL-адресу",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Нова роль",
"reloadRoles": "Перезавантажити ролі",
"nextPage": "Наступна сторінка",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Рядки на сторінку",
"upload": "Виберіть файл для завантаження",
"upload_sub": "або перетягніть файл",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Метадані проекту успішно експортується",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

24
packages/nc-gui/lang/vi.json

@ -104,7 +104,9 @@
"creator": "Người sáng tạo",
"editor": "Biên tập viên",
"commenter": "Bình luận",
"viewer": "Người xem"
"viewer": "Người xem",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
"sqlVIew": "SQL View"
},
@ -200,6 +202,7 @@
"codeSnippet": "Code Snippet"
},
"labels": {
"createdBy": "Created By",
"notifyVia": "Thông báo qua",
"projName": "Tên dự án",
"tableName": "Tên bảng.",
@ -296,7 +299,8 @@
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!"
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
},
"activity": {
"createProject": "Tạo dự án",
@ -341,12 +345,14 @@
"invite": "Mời",
"inviteMore": "Mời thêm",
"inviteTeam": "Mời đội",
"inviteUser": "Invite User",
"inviteToken": "Mời Token.",
"newUser": "Người dùng mới",
"editUser": "Người dùng biên tập",
"deleteUser": "Xóa người dùng khỏi dự án",
"resendInvite": "Gửi lại lời mời e-mail",
"copyInviteURL": "Sao chép Mời URL.",
"copyPasswordResetURL": "Copy password reset URL",
"newRole": "Vai trò mới",
"reloadRoles": "Đóng vai trò",
"nextPage": "Trang tiếp theo",
@ -480,6 +486,10 @@
},
"msg": {
"info": {
"roles": {
"orgCreator": "Creator can create new projects and access any invited project.",
"orgViewer": "Viewer is not allowed to create new projects but they can access any invited project."
},
"footerInfo": "Hàng trên mỗi trang",
"upload": "Chọn tệp để tải lên",
"upload_sub": "hoặc kéo và thả tập tin",
@ -653,7 +663,8 @@
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty."
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible"
},
"toast": {
"exportMetadata": "Metadata dự án xuất khẩu thành công",
@ -683,13 +694,16 @@
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",
"userAddedToProject": "Successfully added user to project",
"userAdded": "Successfully added user",
"userDeletedFromProject": "Successfully deleted user from project",
"inviteEmailSent": "Invite Email sent successfully",
"inviteURLCopied": "Invite URL copied to clipboard",
"passwordResetURLCopied": "Password reset URL copied to clipboard",
"shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details",
@ -699,7 +713,9 @@
"webhookTested": "Webhook tested successfully",
"columnUpdated": "Column updated",
"columnCreated": "Column created",
"passwordChanged": "Password changed successfully. Please login again."
"passwordChanged": "Password changed successfully. Please login again.",
"settingsSaved": "Settings saved successfully",
"roleUpdated": "Role updated successfully"
}
}
}

2
packages/nc-gui/layouts/default.vue

@ -20,7 +20,7 @@ export default {
<template>
<div class="w-full h-full">
<Teleport :to="hasSidebar ? '#nc-sidebar-left' : null" :disabled="!hasSidebar">
<slot :key="$route.name" name="sidebar" />
<slot name="sidebar" />
</Teleport>
<a-layout-content>

2
packages/nc-gui/pages/[projectType]/[projectId]/index.vue

@ -475,7 +475,7 @@ onBeforeUnmount(reset)
to="/account/users"
>
<MdiAt class="mt-1 group-hover:text-accent" />&nbsp;
<div class="prose group-hover:text-primary">
<div class="prose-sm group-hover:text-primary">
<div>Account</div>
<div class="text-xs text-gray-500">{{ email }}</div>
</div>

4
packages/nc-gui/pages/forgot-password.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { definePageMeta, isEmail, reactive, ref, useApi, useI18n } from '#imports'
import { definePageMeta, reactive, ref, useApi, useI18n, validateEmail } from '#imports'
definePageMeta({
requiresAuth: false,
@ -25,7 +25,7 @@ const formRules = {
{
validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => {
if (isEmail(v)) return resolve(true)
if (validateEmail(v)) return resolve(true)
reject(new Error(t('msg.error.signUpRules.emailInvalid')))
})
},

4
packages/nc-gui/pages/signin.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { RuleObject } from 'ant-design-vue/es/form'
import { definePageMeta, isEmail, navigateTo, reactive, ref, useApi, useGlobal, useI18n, useSidebar } from '#imports'
import { definePageMeta, navigateTo, reactive, ref, useApi, useGlobal, useI18n, useSidebar, validateEmail } from '#imports'
definePageMeta({
requiresAuth: false,
@ -30,7 +30,7 @@ const formRules: Record<string, RuleObject[]> = {
{
validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => {
if (isEmail(v)) return resolve()
if (validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid')))
})

15
packages/nc-gui/pages/signup/[[token]].vue

@ -1,6 +1,17 @@
<script setup lang="ts">
import { validatePassword } from 'nocodb-sdk'
import { definePageMeta, isEmail, navigateTo, reactive, ref, useApi, useGlobal, useI18n, useNuxtApp, useRoute } from '#imports'
import {
definePageMeta,
navigateTo,
reactive,
ref,
useApi,
useGlobal,
useI18n,
useNuxtApp,
useRoute,
validateEmail,
} from '#imports'
definePageMeta({
requiresAuth: false,
@ -33,7 +44,7 @@ const formRules = {
{
validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => {
if (!v?.length || isEmail(v)) return resolve(true)
if (!v?.length || validateEmail(v)) return resolve(true)
reject(new Error(t('msg.error.signUpRules.emailInvalid')))
})
},

53
packages/nc-gui/utils/cell.ts

@ -0,0 +1,53 @@
import type { ColumnType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
export const dataTypeLow = (column: ColumnType) => column.dt?.toLowerCase()
export const isBoolean = (abstractType: any) => abstractType === 'boolean'
export const isString = (column: ColumnType, abstractType: any) =>
column.uidt === UITypes.SingleLineText || abstractType === 'string'
export const isTextArea = (column: ColumnType) => column.uidt === UITypes.LongText
export const isInt = (column: ColumnType, abstractType: any) => abstractType === 'integer'
export const isFloat = (column: ColumnType, abstractType: any) => abstractType === 'float' || abstractType === UITypes.Number
export const isDate = (column: ColumnType, abstractType: any) => abstractType === 'date' || column.uidt === UITypes.Date
export const isYear = (column: ColumnType, abstractType: any) => abstractType === 'year' || column.uidt === UITypes.Year
export const isTime = (column: ColumnType, abstractType: any) => abstractType === 'time' || column.uidt === UITypes.Time
export const isDateTime = (column: ColumnType, abstractType: any) =>
abstractType === 'datetime' || column.uidt === UITypes.DateTime
export const isJSON = (column: ColumnType) => column.uidt === UITypes.JSON
export const isEnum = (column: ColumnType) => column.uidt === UITypes.SingleSelect
export const isSingleSelect = (column: ColumnType) => column.uidt === UITypes.SingleSelect
export const isSet = (column: ColumnType) => column.uidt === UITypes.MultiSelect
export const isMultiSelect = (column: ColumnType) => column.uidt === UITypes.MultiSelect
export const isURL = (column: ColumnType) => column.uidt === UITypes.URL
export const isEmail = (column: ColumnType) => column.uidt === UITypes.Email
export const isAttachment = (column: ColumnType) => column.uidt === UITypes.Attachment
export const isRating = (column: ColumnType) => column.uidt === UITypes.Rating
export const isCurrency = (column: ColumnType) => column.uidt === UITypes.Currency
export const isPhoneNumber = (column: ColumnType) => column.uidt === UITypes.PhoneNumber
export const isDecimal = (column: ColumnType) => column.uidt === UITypes.Decimal
export const isDuration = (column: ColumnType) => column.uidt === UITypes.Duration
export const isPercent = (column: ColumnType) => column.uidt === UITypes.Percent
export const isSpecificDBType = (column: ColumnType) => column.uidt === UITypes.SpecificDBType
export const isAutoSaved = (column: ColumnType) =>
[
UITypes.SingleLineText,
UITypes.LongText,
UITypes.PhoneNumber,
UITypes.Email,
UITypes.URL,
UITypes.Number,
UITypes.Decimal,
UITypes.Percent,
UITypes.Count,
UITypes.AutoNumber,
UITypes.SpecificDBType,
UITypes.Geometry,
UITypes.Duration,
].includes(column.uidt as UITypes)
export const isManualSaved = (column: ColumnType) =>
[UITypes.Currency, UITypes.Year, UITypes.Time].includes(column.uidt as UITypes)
export const isPrimary = (column: ColumnType) => !!column.pv
export const isPrimaryKey = (column: ColumnType) => !!column.pk

4
packages/nc-gui/utils/parsers/parserHelpers.ts

@ -1,6 +1,6 @@
import { UITypes } from 'nocodb-sdk'
import { isValidURL } from '~/utils/urlUtils'
import { isEmail } from '~/utils/validation'
import { validateEmail } from '~/utils/validation'
const booleanOptions = [
{ checked: true, unchecked: false },
@ -109,7 +109,7 @@ export const isDecimalType = (colData: []) =>
export const isEmailType = (colData: []) =>
colData.some((v: any) => {
return v && isEmail(v)
return v && validateEmail(v)
})
export const isUrlType = (colData: []) =>

2
packages/nc-gui/utils/userUtils.ts

@ -1,3 +1,4 @@
import { OrgUserRoles } from 'nocodb-sdk'
import { ProjectRole } from '~/lib/enums'
export const projectRoleTagColors = {
@ -6,6 +7,7 @@ export const projectRoleTagColors = {
[ProjectRole.Editor]: '#c2f5e8',
[ProjectRole.Commenter]: '#ffdaf6',
[ProjectRole.Viewer]: '#ffdce5',
[OrgUserRoles.SUPER_ADMIN]: '#f5d7cb',
}
export const projectRoles = [ProjectRole.Creator, ProjectRole.Editor, ProjectRole.Commenter, ProjectRole.Viewer]

2
packages/nc-gui/utils/validation.ts

@ -1,6 +1,6 @@
import { getI18n } from '~/plugins/a.i18n'
export const isEmail = (v: string) =>
export const validateEmail = (v: string) =>
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(v)
export const validateTableName = {

19
packages/nc-gui/utils/virtualCell.ts

@ -0,0 +1,19 @@
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
export const isLTAR = (uidt: string, colOptions: unknown): colOptions is LinkToAnotherRecordType =>
uidt === UITypes.LinkToAnotherRecord
export const isHm = (column: ColumnType) =>
isLTAR(column.uidt, column.colOptions) && column.colOptions.type === RelationTypes.HAS_MANY
export const isMm = (column: ColumnType) =>
isLTAR(column.uidt, column.colOptions) && column.colOptions.type === RelationTypes.MANY_TO_MANY
export const isBt = (column: ColumnType) =>
isLTAR(column.uidt, column.colOptions) && column.colOptions.type === RelationTypes.BELONGS_TO
export const isLookup = (column: ColumnType) => column.uidt === UITypes.Lookup
export const isRollup = (column: ColumnType) => column.uidt === UITypes.Rollup
export const isFormula = (column: ColumnType) => column.uidt === UITypes.Formula
export const isCount = (column: ColumnType) => column.uidt === UITypes.Count

1
packages/nocodb-sdk/src/index.ts

@ -4,6 +4,7 @@ export * from './lib/Api';
export * from './lib/sqlUi';
export * from './lib/globals';
export * from './lib/helperFunctions';
export * from './lib/enums';
export * from './lib/formulaHelpers';
export { default as UITypes, isVirtualCol } from './lib/UITypes';
export { default as CustomAPI } from './lib/CustomAPI';

0
packages/nocodb/src/enums/OrgUserRoles.ts → packages/nocodb-sdk/src/lib/enums.ts

2
packages/nocodb/Dockerfile

@ -36,7 +36,7 @@ COPY ./docker/start-litestream.sh /usr/src/appEntry/start.sh
# reduce node_module size with modclean & removing sqlite deps,
# package built code into app.tar.gz & add execute permission to start.sh
RUN npm ci --omit=dev --quiet \
&& npx modclean --patterns="default:*" --ignore="nc-lib-gui/**,dayjs/**,express-status-monitor/**" --run \
&& npx modclean --patterns="default:*" --ignore="nc-lib-gui/**,dayjs/**,express-status-monitor/**,@azure/msal-node/dist/**" --run \
&& rm -rf ./node_modules/sqlite3/deps \
&& tar -czf ../appEntry/app.tar.gz ./* \
&& chmod +x /usr/src/appEntry/start.sh

1395
packages/nocodb/package-lock.json generated

File diff suppressed because it is too large Load Diff

2
packages/nocodb/package.json

@ -106,7 +106,7 @@
"multer": "^1.4.2",
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-help": "0.2.76",
"nc-help": "0.2.79",
"nc-lib-gui": "0.98.4",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",

13
packages/nocodb/src/lib/cache/RedisCacheMgr.ts vendored

@ -2,6 +2,7 @@ import debug from 'debug';
import CacheMgr from './CacheMgr';
import Redis from 'ioredis';
import { CacheDelDirection, CacheGetType, CacheScope } from '../utils/globals';
const log = debug('nc:cache');
export default class RedisCacheMgr extends CacheMgr {
@ -101,11 +102,15 @@ export default class RedisCacheMgr extends CacheMgr {
log(
`RedisCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}`
);
await Promise.all(
keys.map(async (k) => {
await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT);
})
);
return Promise.all(
keys.map(
async (k) =>
await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT)
)
keys.map(async (k) => {
await this.del(k);
})
);
}

7
packages/nocodb/src/lib/cache/RedisMockCacheMgr.ts vendored

@ -101,12 +101,17 @@ export default class RedisMockCacheMgr extends CacheMgr {
log(
`RedisMockCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}`
);
return Promise.all(
await Promise.all(
keys.map(
async (k) =>
await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT)
)
);
return Promise.all(
keys.map(async (k) => {
await this.del(k);
})
);
}
async getList(scope: string, subKeys: string[]): Promise<any[]> {

374
packages/nocodb/src/lib/db/sql-client/lib/KnexClient.ts

@ -11,7 +11,6 @@ import { promisify } from 'util';
import jsonfile from 'jsonfile';
import path from 'path';
import mkdirp from 'mkdirp';
import Order from './order';
import * as dataHelp from './data.helper';
import SqlClient from './SqlClient';
const evt = new Emit();
@ -892,377 +891,8 @@ class KnexClient extends SqlClient {
getMinMax(_columnObject) {}
async mockDb(args) {
// console.time('mockDb');
const faker = require('faker');
try {
const settings = await jsonfile.readFile(
path.join(args.seedsFolder, '__xseeds.json')
);
await this.dbCacheInitAsyncKnex();
const order = new Order(this.metaDb.erMatrix);
const orders = order.getOrder();
console.log('Insert order by table index: ', orders);
// console.log('Insert order by table name: ');
// order.resetIndex(15, 16);
// orders = order.getOrder();
// console.log('Insert order by table index: ', orders);
// console.log('Insert order by table name: ');
// return;
/** ************** START : reset all tables *************** */
await this.knex.raw('SET foreign_key_checks = 0');
for (let i = 0; i < orders.order.length; ++i) {
const tn = this.metaDb.erIndexTableObj[orders.order[i]];
if (tn !== 'xc_evolutions' && this.metaDb.tables[tn].is_view === null) {
await this.knex.raw(`truncate ${tn}`);
await this.knex.raw(`ALTER TABLE ${tn} AUTO_INCREMENT = 0;`);
}
}
await this.knex.raw('SET foreign_key_checks = 1');
/** ************** END : reset all tables *************** */
// return;
// iterate over each table
for (let i = 0; i < orders.order.length; ++i) {
const tn = this.metaDb.erIndexTableObj[orders.order[i]];
const tableObj = this.metaDb.tables[tn];
if (tn === 'xc_evolutions') {
} else {
/** ************** START : ignore views *************** */
if (this.metaDb.tables[tn].is_view !== null) {
console.log('ignore view', tn);
continue;
}
let numOfRows = +settings.rows.value || 8;
const rows = [];
let fks = [];
let fakerColumns: any = await this.fakerColumnsList({
seedsFolder: args.seedsFolder,
tn: tn,
});
fakerColumns = fakerColumns.data.list;
let maxPks = numOfRows;
if (tableObj.primaryKeys.length) {
maxPks = this._getMaxPksPossible(tableObj.primaryKeys[0]);
}
console.log('MaxPks Possible', maxPks);
if (maxPks < numOfRows) {
numOfRows = maxPks;
}
console.log(`\n\n Preparing to insert ${numOfRows} in ${tn}`);
/** ************** START : create empty rows *************** */
for (let k = 0; k < numOfRows; ++k) {
rows.push({});
}
// iterate over each foreign key in this table
/** ************** START : get FK column values *************** */
for (let j = 0; j < this.metaDb.tables[tn].foreignKeys.length; ++j) {
const fkObj = this.metaDb.tables[tn].foreignKeys[j];
// console.log('\n\tStart : get FK row', fkObj['rtn'] + '.' + fkObj['rcn'] + ' === ' + fkObj['cn']);
fks = await this.knex(fkObj.rtn)
.select(`${fkObj.rcn} as ${fkObj.cn}`)
.limit(numOfRows);
// console.log('fks:', fks);
for (
let rowIndex = 0, fksIndex = 0;
rowIndex < numOfRows && fksIndex < fks.length;
fksIndex++
) {
if (rowIndex < fks.length) {
for (
let l = 0;
l < settings.foreign_key_rows.value && rowIndex < numOfRows;
++l, ++rowIndex
) {
rows[rowIndex][fkObj.cn] = fks[fksIndex][fkObj.cn];
// rows[k+1][fkObj['cn']] = fks[k][fkObj['cn']];
}
} else if (fks.length) {
// rows[k][fkObj['cn']] = fks[fks.length - 1][fkObj['cn']];
break;
} else {
rows[rowIndex][fkObj.cn] = null;
}
}
// for (let k = 0; k < numOfRows; ++k) {
// if (k < fks.length) {
// rows[k][fkObj['cn']] = fks[k][fkObj['cn']];
// } else {
// if (fks.length) {
// //rows[k][fkObj['cn']] = fks[fks.length - 1][fkObj['cn']];
// break;
// } else {
// rows[k][fkObj['cn']] = null;
// }
// }
//
// }
// console.log('\tEnd : get FK row', fkObj['rtn'] + '.' + fkObj['rcn'] + ' === ' + fkObj['cn']);
}
/** ************** START : populate columns of the table *************** */
for (let j = 0; j < this.metaDb.tables[tn].columns.length; ++j) {
const colObj = this.metaDb.tables[tn].columns[j];
const colUiObj = this.metaDb.tables[tn].uiModel.columns[j];
const fakerColObj = fakerColumns.find(
(col) => col.cn === colObj.cn
);
console.log(
'\tColumn: ',
colObj.cn,
' of type ',
colObj.dt,
colObj.np,
colObj.clen
);
const dt = this.getKnexDataTypeMock(colObj.dt);
const numDict = {};
const floatDict = {};
const strDict = {};
for (let k = 0; k < numOfRows; ++k) {
// console.log(this.knex(tn).insert(rows).toSQL());
console.log(rows);
switch (dt) {
case 'year':
rows[k][colObj.cn] = Math.floor(Math.random() * 180) + 1920;
break;
case 'enum':
{
const enums = fakerColObj.dtxp
.split(',')
.map((v) => v.slice(1, -1));
const enumIndex = Math.floor(Math.random() * enums.length);
rows[k][colObj.cn] = enums[enumIndex];
}
break;
case 'integer':
if (this._isColumnPrimary(colObj) && colUiObj.ai) {
// primary key is auto inc - ignore creating from faker
// rows[k][colObj['cn']] = 0;
} else if (this._isColumnForeignKey(tableObj, colObj.cn)) {
// foreign key - ignore creating from faker
} else {
while (1) {
console.log('..');
let num = this._getMaxNumPossible(fakerColObj);
num = Math.floor(Math.random() * num);
// let max = Math.pow(2, colObj['np'])
// num = num % max;
if (
(colObj.ck === 'UNI' ||
colObj.ck === 'MUL' ||
this._isColumnPrimaryForInserting(
tableObj,
colObj
)) &&
num in numDict
) {
// console.log('>> num', num, numOfRows);
} else {
rows[k][colObj.cn] = num;
numDict[num] = num;
break;
}
}
}
break;
case 'float':
while (1) {
let num = Math.floor(Math.random() * 2147483647);
const max = Math.pow(2, fakerColObj.dtxp);
num %= max;
if (
(colObj.ck === 'UNI' ||
colObj.ck === 'MUL' ||
this._isColumnPrimaryForInserting(tableObj, colObj)) &&
num in floatDict
) {
// console.log('>> num', num, numOfRows);
} else {
rows[k][colObj.cn] = num;
floatDict[num] = num;
break;
}
}
// rows[k][colObj['cn']] = faker.random.number();
break;
case 'date':
rows[k][colObj.cn] = faker.date.past();
break;
case 'blob':
rows[k][colObj.cn] = faker.lorem.sentence();
break;
case 'boolean':
rows[k][colObj.cn] = faker.random.boolean();
break;
case 'geometry':
rows[k][colObj.cn] = this.knex.raw(
faker.fake('POINT({{random.number}}, {{random.number}})')
);
break;
case 'point':
rows[k][colObj.cn] = this.knex.raw(
faker.fake('POINT({{random.number}}, {{random.number}})')
);
break;
case 'linestring':
rows[k][colObj.cn] = this.knex.raw(
faker.fake(
'LineString(POINT({{random.number}}, {{random.number}}), POINT({{random.number}}, {{random.number}}))'
)
);
break;
case 'polygon':
rows[k][colObj.cn] = this.knex.raw(
faker.fake(
"ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))')"
)
);
break;
case 'multipoint':
rows[k][colObj.cn] = this.knex.raw(
faker.fake(
"ST_MPointFromText('MULTIPOINT ({{random.number}} {{random.number}}, {{random.number}} {{random.number}}, {{random.number}} {{random.number}})')"
)
);
break;
case 'multilinestring':
rows[k][colObj.cn] = this.knex.raw(
faker.fake(
"ST_GeomFromText('MultiLineString(({{random.number}} {{random.number}}, {{random.number}} {{random.number}}), ({{random.number}} {{random.number}}, {{random.number}} {{random.number}}))')"
)
);
break;
case 'multipolygon':
rows[k][colObj.cn] = this.knex.raw(
faker.fake(
"ST_GeomFromText('MultiPolygon(((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1)))')"
)
);
break;
case 'json':
rows[k][colObj.cn] = JSON.stringify({
name: faker.name.firstName(),
suffix: faker.name.suffix(),
});
break;
case 'bit':
{
const num = Math.floor(
Math.random() * Math.pow(2, colObj.np || 6)
);
rows[k][colObj.cn] = num;
}
break;
case 'text':
rows[k][colObj.cn] = faker.lorem.sentence();
break;
case 'string':
default:
if (this._isColumnForeignKey(tableObj, colObj.cn)) {
} else {
while (1) {
const fakerColumn = fakerColumns.find(
(col) => col.cn === colObj.cn
);
const fakerFuncs = fakerColumn.fakerFunction
? fakerColumn.fakerFunction.split('.')
: [];
let str = faker.internet.email();
if (fakerFuncs.length) {
// str = faker[fakerFuncs[0]][fakerFuncs[1]]();
}
let max = 1;
if (colObj.clen) {
max = colObj.clen;
if (max < str.length) {
// max = str.length;
}
str = str.slice(0, max);
}
if (
(colObj.ck === 'UNI' ||
this._isColumnPrimaryForInserting(
tableObj,
colObj
)) &&
str in strDict
) {
} else {
rows[k][colObj.cn] = str;
strDict[str] = 1;
break;
}
}
}
break;
}
}
}
for (let i = 0; i < rows.length; ++i) {
// for (let key in rows[i]) {
// console.log(key,rows[i][key]);
// }
await this.knex(tn).insert(rows[i]);
}
// await knex.raw(knex(tn).insert(rows).toSQL() + " ON DUPLICATE KEY UPDATE " +
// Object.getOwnPropertyNames(rows).map((field) => `${field}=VALUES(${field})`).join(", "))
this.emit(`Inserted '${rows.length}' rows in 'table.${tn}'`);
console.log('End: ', tn);
}
// console.log(orders.order.length, tablesAsArr.length);
//
// let cycle = Object.keys(orders.cycle);
//
// for (let i = 0; i < cycle.length; ++i) {
// console.log(tablesAsArr[cycle[i]]);
// }
}
} catch (e) {
console.log('Error in mockDb : ', e);
throw e;
}
// console.timeEnd('mockDb')
async mockDb(_args) {
// todo: remove method
}
async dbCacheInitAsyncKnex(_cbk = null) {

2
packages/nocodb/src/lib/meta/api/apiTokenApis.ts

@ -1,5 +1,5 @@
import { Request, Response, Router } from 'express';
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import { Tele } from 'nc-help';
import { NcError } from '../helpers/catchError';
import ncMetaAclMw from '../helpers/ncMetaAclMw';

2
packages/nocodb/src/lib/meta/api/ee/orgTokenApis.ts

@ -1,4 +1,4 @@
import { OrgUserRoles } from '../../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import ApiToken from '../../../models/ApiToken';
import { PagedResponseImpl } from '../../helpers/PagedResponse';

2
packages/nocodb/src/lib/meta/api/orgLicenseApis.ts

@ -1,5 +1,5 @@
import { Router } from 'express';
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import { NC_LICENSE_KEY } from '../../constants'
import Store from '../../models/Store';
import { metaApiMetrics } from '../helpers/apiMetrics';

2
packages/nocodb/src/lib/meta/api/orgTokenApis.ts

@ -1,5 +1,5 @@
import { Request, Response, Router } from 'express';
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import ApiToken from '../../models/ApiToken';
import { Tele } from 'nc-help';
import { metaApiMetrics } from '../helpers/apiMetrics';

11
packages/nocodb/src/lib/meta/api/orgUserApis.ts

@ -6,7 +6,7 @@ import {
} from 'nocodb-sdk';
import { v4 as uuidv4 } from 'uuid';
import validator from 'validator';
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import { NC_APP_SETTINGS } from '../../constants';
import Audit from '../../models/Audit';
import ProjectUser from '../../models/ProjectUser';
@ -42,7 +42,12 @@ async function userUpdate(req, res) {
NcError.badRequest('Cannot update super admin roles');
}
res.json(await User.update(req.params.userId, updateBody));
res.json(
await User.update(req.params.userId, {
...updateBody,
token_version: null,
})
);
}
async function userDelete(req, res) {
@ -55,7 +60,7 @@ async function userDelete(req, res) {
}
// delete project user entry and assign to super admin
const projectUsers = await ProjectUser.getProjectsList(
const projectUsers = await ProjectUser.getProjectsIdList(
req.params.userId,
ncMeta
);

11
packages/nocodb/src/lib/meta/api/projectApis.ts

@ -1,4 +1,5 @@
import { Request, Response } from 'express';
import { OrgUserRoles, ProjectType } from 'nocodb-sdk';
import Project from '../../models/Project';
import { ModelTypes, ProjectListType, UITypes } from 'nocodb-sdk';
import DOMPurify from 'isomorphic-dompurify';
@ -71,16 +72,18 @@ export async function projectUpdate(
}
export async function projectList(
req: Request<any, any, any>,
req: Request<any> & { user: { id: string; roles: string } },
res: Response<ProjectListType>,
next
) {
try {
const projects = await Project.list(req.query);
const projects = req.user?.roles?.includes(OrgUserRoles.SUPER_ADMIN)
? await Project.list(req.query)
: await ProjectUser.getProjectsList(req.user.id, req.query);
res // todo: pagination
.json(
new PagedResponseImpl(projects, {
new PagedResponseImpl(projects as ProjectType[], {
count: projects.length,
limit: projects.length,
})
@ -92,7 +95,7 @@ export async function projectList(
}
export async function projectDelete(
req: Request<any, any, any>,
req: Request<any>,
res: Response<ProjectListType>
) {
const result = await Project.softDelete(req.params.projectId);

2
packages/nocodb/src/lib/meta/api/projectUserApis.ts

@ -1,4 +1,4 @@
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import { Tele } from 'nc-help';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { Router } from 'express';

2
packages/nocodb/src/lib/meta/api/userApi/initStrategies.ts

@ -1,4 +1,4 @@
import { OrgUserRoles } from '../../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import User from '../../../models/User';
import ProjectUser from '../../../models/ProjectUser';
import { promisify } from 'util';

2
packages/nocodb/src/lib/meta/api/userApi/userApis.ts

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { TableType, validatePassword } from 'nocodb-sdk';
import { OrgUserRoles } from '../../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import { NC_APP_SETTINGS } from '../../../constants';
import Store from '../../../models/Store';
import { Tele } from 'nc-help';

2
packages/nocodb/src/lib/meta/helpers/ncMetaAclMw.ts

@ -1,4 +1,4 @@
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import projectAcl from '../../utils/projectAcl';
import { NextFunction, Request, Response } from 'express';
import catchError, { NcError } from './catchError';

4
packages/nocodb/src/lib/models/Project.ts

@ -194,6 +194,8 @@ export default class Project implements ProjectType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${o.id}`);
}
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.del(CacheScope.INSTANCE_META);
// remove item in cache list
@ -292,6 +294,8 @@ export default class Project implements ProjectType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${project.id}`);
}
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.deepDel(
CacheScope.PROJECT,
`${CacheScope.PROJECT}:${projectId}`,

65
packages/nocodb/src/lib/models/ProjectUser.ts

@ -1,3 +1,4 @@
import { ProjectType } from 'nocodb-sdk';
import {
// CacheDelDirection,
CacheGetType,
@ -35,6 +36,12 @@ export default class ProjectUser {
true
);
// reset all user projects cache
await NocoCache.delAll(
CacheScope.USER_PROJECT,
`${projectUser.fk_user_id}:*`
);
return this.get(project_id, fk_user_id, ncMeta);
}
@ -165,7 +172,7 @@ export default class ProjectUser {
);
}
static async delete(projectId: string, userId, ncMeta = Noco.ncMeta) {
static async delete(projectId: string, userId: string, ncMeta = Noco.ncMeta) {
// await NocoCache.deepDel(
// CacheScope.PROJECT_USER,
// `${CacheScope.PROJECT_USER}:${projectId}:${userId}`,
@ -177,6 +184,20 @@ export default class ProjectUser {
if (email) {
await NocoCache.delAll(CacheScope.USER, `${email}*`);
}
// remove project from user project list cache
let cachedProjectList = await NocoCache.getList(CacheScope.USER_PROJECT, [
userId,
]);
if (cachedProjectList?.length) {
cachedProjectList = cachedProjectList.filter((p) => p.id !== projectId);
await NocoCache.setList(
CacheScope.USER_PROJECT,
[userId],
cachedProjectList
);
}
await NocoCache.del(`${CacheScope.PROJECT_USER}:${projectId}:${userId}`);
return await ncMeta.metaDelete(null, null, MetaTable.PROJECT_USERS, {
fk_user_id: userId,
@ -184,7 +205,7 @@ export default class ProjectUser {
});
}
static async getProjectsList(
static async getProjectsIdList(
userId: string,
ncMeta = Noco.ncMeta
): Promise<ProjectUser[]> {
@ -192,4 +213,44 @@ export default class ProjectUser {
condition: { fk_user_id: userId },
});
}
static async getProjectsList(
userId: string,
_params: any,
ncMeta = Noco.ncMeta
): Promise<ProjectType[]> {
// todo: pagination
let projectList = await NocoCache.getList(CacheScope.USER_PROJECT, [
userId,
]);
if (projectList.length) {
return projectList;
}
projectList = await ncMeta
.knex(MetaTable.PROJECT)
.select(`${MetaTable.PROJECT}.*`)
.innerJoin(MetaTable.PROJECT_USERS, function () {
this.on(
`${MetaTable.PROJECT_USERS}.project_id`,
`${MetaTable.PROJECT}.id`
);
this.andOn(
`${MetaTable.PROJECT_USERS}.fk_user_id`,
ncMeta.knex.raw('?', [userId])
);
})
.where(function () {
this.where(`${MetaTable.PROJECT}.deleted`, false).orWhereNull(
`${MetaTable.PROJECT}.deleted`
);
});
if (projectList?.length) {
await NocoCache.setList(CacheScope.USER_PROJECT, [userId], projectList);
}
return projectList;
}
}

1
packages/nocodb/src/lib/utils/globals.ts

@ -136,6 +136,7 @@ export enum CacheScope {
MODEL_ROLE_VISIBILITY = 'modelRoleVisibility',
API_TOKEN = 'apiToken',
INSTANCE_META = 'instanceMeta',
USER_PROJECT = 'userProject',
}
export enum CacheGetType {
TYPE_ARRAY = 'TYPE_ARRAY',

2
packages/nocodb/src/lib/utils/projectAcl.ts

@ -1,4 +1,4 @@
import { OrgUserRoles } from '../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
export default {
owner: {

2
packages/nocodb/src/lib/version-upgrader/ncProjectRolesUpgrader.ts

@ -1,4 +1,4 @@
import { OrgUserRoles } from '../../enums/OrgUserRoles';
import { OrgUserRoles } from 'nocodb-sdk';
import { NC_APP_SETTINGS } from '../constants';
import Store from '../models/Store';
import { MetaTable } from '../utils/globals';

2
packages/nocodb/tests/unit/rest/tests/org.test.ts

@ -1,7 +1,7 @@
import { expect } from 'chai'
import 'mocha'
import request from 'supertest'
import { OrgUserRoles } from '../../../../src/enums/OrgUserRoles'
import { OrgUserRoles } from 'nocodb-sdk'
import init from '../../init'
function authTests() {

Loading…
Cancel
Save