Browse Source

Merge branch 'develop' into mvp-mobile-layout-and-code-scanner

pull/5114/head
Daniel Spaude 2 years ago
parent
commit
03e6ecccf9
No known key found for this signature in database
GPG Key ID: 654A3D1FA4F35FFE
  1. 9
      .github/workflows/release-timely-executables.yml
  2. 4
      packages/nc-gui/assets/style.scss
  3. 155
      packages/nc-gui/components/cell/GeoData.vue
  4. 12
      packages/nc-gui/components/cell/SingleSelect.vue
  5. 9
      packages/nc-gui/components/dlg/QuickImport.vue
  6. 11
      packages/nc-gui/components/dlg/TableCreate.vue
  7. 66
      packages/nc-gui/components/dlg/ViewCreate.vue
  8. 42
      packages/nc-gui/components/shared-view/Map.vue
  9. 6
      packages/nc-gui/components/smartsheet/Cell.vue
  10. 266
      packages/nc-gui/components/smartsheet/Map.vue
  11. 12
      packages/nc-gui/components/smartsheet/Toolbar.vue
  12. 13
      packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
  13. 9
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  14. 4
      packages/nc-gui/components/smartsheet/header/CellIcon.ts
  15. 216
      packages/nc-gui/components/smartsheet/sidebar/MenuBottom.vue
  16. 15
      packages/nc-gui/components/smartsheet/sidebar/toolbar/GeodataSwitcher.vue
  17. 4
      packages/nc-gui/components/smartsheet/sidebar/toolbar/index.vue
  18. 3
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  19. 113
      packages/nc-gui/components/smartsheet/toolbar/MappedBy.vue
  20. 9
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  21. 3
      packages/nc-gui/components/smartsheet/toolbar/SharedViewList.vue
  22. 55
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  23. 5
      packages/nc-gui/components/tabs/Smartsheet.vue
  24. 16
      packages/nc-gui/components/template/Editor.vue
  25. 35
      packages/nc-gui/components/virtual-cell/components/ListItems.vue
  26. 7
      packages/nc-gui/composables/useAttachment.ts
  27. 182
      packages/nc-gui/composables/useMapViewDataStore.ts
  28. 2
      packages/nc-gui/composables/useMultiSelect/index.ts
  29. 3
      packages/nc-gui/composables/useSmartsheetStore.ts
  30. 15
      packages/nc-gui/composables/useViewColumns.ts
  31. 21
      packages/nc-gui/lang/ar.json
  32. 21
      packages/nc-gui/lang/bn_IN.json
  33. 21
      packages/nc-gui/lang/cs.json
  34. 21
      packages/nc-gui/lang/da.json
  35. 21
      packages/nc-gui/lang/de.json
  36. 20
      packages/nc-gui/lang/en.json
  37. 21
      packages/nc-gui/lang/es.json
  38. 21
      packages/nc-gui/lang/eu.json
  39. 21
      packages/nc-gui/lang/fa.json
  40. 21
      packages/nc-gui/lang/fi.json
  41. 21
      packages/nc-gui/lang/fr.json
  42. 21
      packages/nc-gui/lang/he.json
  43. 21
      packages/nc-gui/lang/hi.json
  44. 21
      packages/nc-gui/lang/hr.json
  45. 21
      packages/nc-gui/lang/id.json
  46. 21
      packages/nc-gui/lang/it.json
  47. 21
      packages/nc-gui/lang/ja.json
  48. 21
      packages/nc-gui/lang/ko.json
  49. 21
      packages/nc-gui/lang/lv.json
  50. 21
      packages/nc-gui/lang/nl.json
  51. 21
      packages/nc-gui/lang/no.json
  52. 21
      packages/nc-gui/lang/pl.json
  53. 21
      packages/nc-gui/lang/pt.json
  54. 21
      packages/nc-gui/lang/pt_BR.json
  55. 21
      packages/nc-gui/lang/ru.json
  56. 21
      packages/nc-gui/lang/sk.json
  57. 21
      packages/nc-gui/lang/sl.json
  58. 21
      packages/nc-gui/lang/sv.json
  59. 21
      packages/nc-gui/lang/th.json
  60. 21
      packages/nc-gui/lang/tr.json
  61. 21
      packages/nc-gui/lang/uk.json
  62. 21
      packages/nc-gui/lang/vi.json
  63. 53
      packages/nc-gui/lang/zh-Hans.json
  64. 21
      packages/nc-gui/lang/zh-Hant.json
  65. 1
      packages/nc-gui/lib/constants.ts
  66. 1
      packages/nc-gui/lib/enums.ts
  67. 1
      packages/nc-gui/lib/types.ts
  68. 116
      packages/nc-gui/package-lock.json
  69. 6
      packages/nc-gui/package.json
  70. 4
      packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue
  71. 35
      packages/nc-gui/pages/[projectType]/map/[viewId]/index.vue
  72. 9
      packages/nc-gui/pages/index/index/create.vue
  73. 2
      packages/nc-gui/utils/cell.ts
  74. 6
      packages/nc-gui/utils/columnUtils.ts
  75. 3
      packages/nc-gui/utils/geoDataUtils.ts
  76. 1
      packages/nc-gui/utils/index.ts
  77. 693
      packages/nc-gui/utils/mimeTypeUtils.ts
  78. 3
      packages/nc-gui/utils/viewUtils.ts
  79. 2
      packages/nc-lib-gui/package.json
  80. 75
      packages/noco-docs/content/en/setup-and-usages/column-types.md
  81. 4
      packages/nocodb-sdk/package-lock.json
  82. 2
      packages/nocodb-sdk/package.json
  83. 83
      packages/nocodb-sdk/src/lib/Api.ts
  84. 1
      packages/nocodb-sdk/src/lib/UITypes.ts
  85. 1
      packages/nocodb-sdk/src/lib/globals.ts
  86. 6
      packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts
  87. 20
      packages/nocodb/package-lock.json
  88. 4
      packages/nocodb/package.json
  89. 2
      packages/nocodb/src/lib/Noco.ts
  90. 2
      packages/nocodb/src/lib/db/sql-client/lib/oracle/OracleClient.ts
  91. 8
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/helpers/getAst.ts
  92. 19
      packages/nocodb/src/lib/db/sql-mgr/SqlMgr.ts
  93. 2
      packages/nocodb/src/lib/db/sql-mgr/v2/SqlMgrv2.ts
  94. 2
      packages/nocodb/src/lib/db/sql-mgr/v2/SqlMgrv2Trans.ts
  95. 7
      packages/nocodb/src/lib/db/sql-migrator/lib/KnexMigratorv2.ts
  96. 18
      packages/nocodb/src/lib/db/sql-migrator/lib/KnexMigratorv2Tans.ts
  97. 2
      packages/nocodb/src/lib/meta/NcMetaMgr.ts
  98. 79
      packages/nocodb/src/lib/meta/api/dataApis/bulkDataAliasApis.ts
  99. 2
      packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts
  100. 2
      packages/nocodb/src/lib/meta/api/index.ts
  101. Some files were not shown because too many files have changed in this diff Show More

9
.github/workflows/release-timely-executables.yml

@ -62,6 +62,10 @@ jobs:
./make.sh
sudo cp ./ldid /usr/local/bin
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Update nocodb-timely
env:
TAG: ${{ github.event.inputs.tag || inputs.tag }}
@ -75,11 +79,6 @@ jobs:
git tag $TAG
git push --tags
- uses: actions/setup-node@v3
with:
node-version: 16
- name : Install dependencies and build executables
run: |
# install npm dependendencies

4
packages/nc-gui/assets/style.scss

@ -287,6 +287,10 @@ a {
@apply !shadow-none rounded hover:(ring-1 ring-primary ring-opacity-100) focus:(ring-1 ring-accent ring-opacity-100);
}
.nc-warning-info {
@apply !shadow-none rounded ring-1 ring-red-600
}
.ant-modal {
@apply !top-[50px];
}

155
packages/nc-gui/components/cell/GeoData.vue

@ -0,0 +1,155 @@
<script lang="ts" setup>
import type { GeoLocationType } from 'nocodb-sdk'
import { Modal as AModal, latLongToJoinedString, useVModel } from '#imports'
interface Props {
modelValue?: string | null
}
interface Emits {
(event: 'update:modelValue', model: GeoLocationType): void
}
const props = defineProps<Props>()
const emits = defineEmits<Emits>()
const vModel = useVModel(props, 'modelValue', emits)
let isExpanded = $ref(false)
let isLoading = $ref(false)
let isLocationSet = $ref(false)
const [latitude, longitude] = (vModel.value || '').split(';')
const latLongStr = computed(() => {
const [latitude, longitude] = (vModel.value || '').split(';')
if (latitude) isLocationSet = true
return latitude && longitude ? `${latitude}; ${longitude}` : 'Set location'
})
const formState = reactive({
latitude,
longitude,
})
const handleFinish = () => {
vModel.value = latLongToJoinedString(parseFloat(formState.latitude), parseFloat(formState.longitude))
isExpanded = false
}
const clear = () => {
isExpanded = false
formState.latitude = latitude
formState.longitude = longitude
}
const onClickSetCurrentLocation = () => {
isLoading = true
const onSuccess = (position) => {
const crd = position.coords
formState.latitude = crd.latitude
formState.longitude = crd.longitude
isLoading = false
}
const onError = (err) => {
console.error(`ERROR(${err.code}): ${err.message}`)
isLoading = false
}
const options = {
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 2000,
}
navigator.geolocation.getCurrentPosition(onSuccess, onError, options)
}
</script>
<template>
<a-dropdown :is="isExpanded ? AModal : 'div'" v-model:visible="isExpanded" trigger="click">
<div
v-if="!isLocationSet"
class="group cursor-pointer flex gap-1 items-center mx-auto max-w-32 justify-center active:(ring ring-accent ring-opacity-100) rounded border-1 p-1 shadow-sm hover:(bg-primary bg-opacity-10) dark:(!bg-slate-500)"
>
<div class="flex items-center gap-2" data-testid="nc-geo-data-set-location-button">
<MdiMapMarker class="transform dark:(!text-white) group-hover:(!text-accent scale-120) text-gray-500 text-[0.75rem]" />
<div class="group-hover:text-primary text-gray-500 dark:text-gray-200 dark:group-hover:!text-white text-xs">
{{ latLongStr }}
</div>
</div>
</div>
<div v-else data-testid="nc-geo-data-lat-long-set">{{ latLongStr }}</div>
<template #overlay>
<a-form :model="formState" class="flex flex-col" @finish="handleFinish">
<a-form-item>
<div class="flex mt-4 items-center mx-2">
<div class="mr-2">{{ $t('labels.lat') }}:</div>
<a-input
v-model:value="formState.latitude"
data-testid="nc-geo-data-latitude"
type="number"
step="0.0000001"
:min="-90"
required
:max="90"
@keydown.stop
@selectstart.capture.stop
@mousedown.stop
/>
</div>
</a-form-item>
<a-form-item>
<div class="flex items-center mx-2">
<div class="mr-2">{{ $t('labels.lng') }}:</div>
<a-input
v-model:value="formState.longitude"
data-testid="nc-geo-data-longitude"
type="number"
step="0.0000001"
required
:min="-180"
:max="180"
@keydown.stop
@selectstart.capture.stop
@mousedown.stop
/>
</div>
</a-form-item>
<a-form-item>
<div class="flex items-center mr-2">
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin text-gray-500': isLoading }" />
<a-button class="ml-2" @click="onClickSetCurrentLocation">{{ $t('labels.yourLocation') }}</a-button>
</div>
</a-form-item>
<a-form-item>
<div class="ml-auto mr-2">
<a-button type="text" @click="clear">{{ $t('general.cancel') }}</a-button>
<a-button type="primary" html-type="submit" data-testid="nc-geo-data-save">{{ $t('general.submit') }}</a-button>
</div>
</a-form-item>
</a-form>
</template>
</a-dropdown>
</template>
<style scoped lang="scss">
input[type='number']:focus {
@apply ring-transparent;
}
input[type='number'] {
width: 180px;
}
.ant-form-item {
margin-bottom: 1rem;
}
.ant-dropdown-menu {
align-items: flex-end;
}
</style>

12
packages/nc-gui/components/cell/SingleSelect.vue

@ -247,12 +247,12 @@ useEventListener(document, 'click', handleClose, true)
<a-select
ref="aselect"
v-model:value="vModel"
class="w-full"
class="w-full overflow-hidden"
:class="{ 'caret-transparent': !hasEditRoles }"
:allow-clear="!column.rqd && editAllowed"
:bordered="false"
:open="isOpen && (active || editable)"
:disabled="readOnly || !(active || editable)"
:open="isOpen && editAllowed"
:disabled="readOnly || !editAllowed"
:show-arrow="hasEditRoles && !readOnly && (editable || (active && vModel === null))"
:dropdown-class-name="`nc-dropdown-single-select-cell ${isOpen && (active || editable) ? 'active' : ''}`"
:show-search="isOpen && (active || editable)"
@ -328,6 +328,12 @@ useEventListener(document, 'click', handleClose, true)
@apply !px-0;
}
:deep(.ant-select-selection-search) {
// following a-select with mode = multiple | tags
// initial width will block @mouseover in Grid.vue
@apply !w-[5px];
}
:deep(.ant-select-selection-search-input) {
@apply !text-xs;
}

9
packages/nc-gui/components/dlg/QuickImport.vue

@ -31,7 +31,7 @@ interface Props {
importDataOnly?: boolean
}
const { importType, importDataOnly = false, ...rest } = defineProps<Props>()
const { importType, importDataOnly = false, baseId, ...rest } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
@ -61,7 +61,7 @@ const isParsingData = ref(false)
const useForm = Form.useForm
const importState = reactive({
const defaultImportState = {
fileList: [] as importFileList | streamImportFileList,
url: '',
jsonEditor: {},
@ -72,7 +72,8 @@ const importState = reactive({
firstRowAsHeaders: true,
shouldImportData: true,
},
})
}
const importState = reactive(defaultImportState)
const isImportTypeJson = computed(() => importType === 'json')
@ -176,6 +177,8 @@ async function handleImport() {
return message.error(await extractSdkResponseErrorMsg(e))
} finally {
importLoading.value = false
templateEditorModal.value = false
Object.assign(importState, defaultImportState)
}
dialogShow.value = false
}

11
packages/nc-gui/components/dlg/TableCreate.vue

@ -78,14 +78,19 @@ const systemColumnsCheckboxInfo = SYSTEM_COLUMNS.map((c, index) => ({
disabled: index === 0,
}))
const creating = ref(false)
const _createTable = async () => {
try {
creating.value = true
await validate()
await createTable()
} catch (e: any) {
e.errorFields.map((f: Record<string, any>) => message.error(f.errors.join(',')))
if (e.errorFields.length) return
} finally {
creating.value = false
}
await createTable()
}
onMounted(() => {
@ -109,7 +114,9 @@ onMounted(() => {
<template #footer>
<a-button key="back" size="large" @click="dialogShow = false">{{ $t('general.cancel') }}</a-button>
<a-button key="submit" size="large" type="primary" @click="_createTable">{{ $t('general.submit') }}</a-button>
<a-button key="submit" size="large" type="primary" :loading="creating" @click="_createTable"
>{{ $t('general.submit') }}
</a-button>
</template>
<div class="pl-10 pr-10 pt-5">

66
packages/nc-gui/components/dlg/ViewCreate.vue

@ -2,7 +2,7 @@
import type { ComponentPublicInstance } from '@vue/runtime-core'
import type { Form as AntForm, SelectProps } from 'ant-design-vue'
import { capitalize } from '@vue/runtime-core'
import type { FormType, GalleryType, GridType, KanbanType, TableType, ViewType } from 'nocodb-sdk'
import type { FormType, GalleryType, GridType, KanbanType, MapType, TableType, ViewType } from 'nocodb-sdk'
import { UITypes, ViewTypes } from 'nocodb-sdk'
import {
computed,
@ -25,13 +25,14 @@ interface Props {
title?: string
selectedViewId?: string
groupingFieldColumnId?: string
geoDataFieldColumnId?: string
views: ViewType[]
meta: TableType
}
interface Emits {
(event: 'update:modelValue', value: boolean): void
(event: 'created', value: GridType | KanbanType | GalleryType | FormType): void
(event: 'created', value: GridType | KanbanType | GalleryType | FormType | MapType): void
}
interface Form {
@ -40,9 +41,10 @@ interface Form {
copy_from_id: string | null
// for kanban view only
fk_grp_col_id: string | null
fk_geo_data_col_id: string | null
}
const { views = [], meta, selectedViewId, groupingFieldColumnId, ...props } = defineProps<Props>()
const { views = [], meta, selectedViewId, groupingFieldColumnId, geoDataFieldColumnId, ...props } = defineProps<Props>()
const emits = defineEmits<Emits>()
@ -61,9 +63,10 @@ const form = reactive<Form>({
type: props.type,
copy_from_id: null,
fk_grp_col_id: null,
fk_geo_data_col_id: null,
})
const singleSelectFieldOptions = ref<SelectProps['options']>([])
const viewSelectFieldOptions = ref<SelectProps['options']>([])
const viewNameRules = [
// name is required
@ -72,7 +75,7 @@ const viewNameRules = [
{
validator: (_: unknown, v: string) =>
new Promise((resolve, reject) => {
views.every((v1) => ((v1 as GridType | KanbanType | GalleryType).alias || v1.title) !== v)
views.every((v1) => ((v1 as GridType | KanbanType | GalleryType | MapType).alias || v1.title) !== v)
? resolve(true)
: reject(new Error(`View name should be unique`))
}),
@ -80,10 +83,9 @@ const viewNameRules = [
},
]
const groupingFieldColumnRules = [
// name is required
{ required: true, message: `${t('general.groupingField')} ${t('general.required')}` },
]
const groupingFieldColumnRules = [{ required: true, message: `${t('general.groupingField')} ${t('general.required')}` }]
const geoDataFieldColumnRules = [{ required: true, message: `${t('general.geoDataField')} ${t('general.required')}` }]
const typeAlias = computed(
() =>
@ -92,6 +94,7 @@ const typeAlias = computed(
[ViewTypes.GALLERY]: 'gallery',
[ViewTypes.FORM]: 'form',
[ViewTypes.KANBAN]: 'kanban',
[ViewTypes.MAP]: 'map',
}[props.type]),
)
@ -113,7 +116,7 @@ function init() {
// preset the grouping field column
if (props.type === ViewTypes.KANBAN) {
singleSelectFieldOptions.value = meta
viewSelectFieldOptions.value = meta
.columns!.filter((el) => el.uidt === UITypes.SingleSelect)
.map((field) => {
return {
@ -127,7 +130,26 @@ function init() {
form.fk_grp_col_id = groupingFieldColumnId
} else {
// take the first option
form.fk_grp_col_id = singleSelectFieldOptions.value?.[0]?.value as string
form.fk_grp_col_id = viewSelectFieldOptions.value?.[0]?.value as string
}
}
if (props.type === ViewTypes.MAP) {
viewSelectFieldOptions.value = meta
.columns!.filter((el) => el.uidt === UITypes.GeoData)
.map((field) => {
return {
value: field.id,
label: field.title,
}
})
if (geoDataFieldColumnId) {
// take from the one from copy view
form.fk_geo_data_col_id = geoDataFieldColumnId
} else {
// take the first option
form.fk_geo_data_col_id = viewSelectFieldOptions.value?.[0]?.value as string
}
}
@ -150,7 +172,7 @@ async function onSubmit() {
if (!_meta || !_meta.id) return
try {
let data: GridType | KanbanType | GalleryType | FormType | null = null
let data: GridType | KanbanType | GalleryType | FormType | MapType | null = null
switch (form.type) {
case ViewTypes.GRID:
@ -164,6 +186,9 @@ async function onSubmit() {
break
case ViewTypes.KANBAN:
data = await api.dbView.kanbanCreate(_meta.id, form)
break
case ViewTypes.MAP:
data = await api.dbView.mapCreate(_meta.id, form)
}
if (data) {
@ -207,12 +232,27 @@ async function onSubmit() {
<a-select
v-model:value="form.fk_grp_col_id"
class="w-full nc-kanban-grouping-field-select"
:options="singleSelectFieldOptions"
:options="viewSelectFieldOptions"
:disabled="groupingFieldColumnId"
placeholder="Select a Grouping Field"
not-found-content="No Single Select Field can be found. Please create one first."
/>
</a-form-item>
<a-form-item
v-if="form.type === ViewTypes.MAP"
:label="$t('general.geoDataField')"
name="fk_geo_data_col_id"
:rules="geoDataFieldColumnRules"
>
<a-select
v-model:value="form.fk_geo_data_col_id"
class="w-full"
:options="viewSelectFieldOptions"
:disabled="geoDataFieldColumnId"
placeholder="Select a GeoData Field"
not-found-content="No GeoData Field can be found. Please create one first."
/>
</a-form-item>
</a-form>
<template #footer>

42
packages/nc-gui/components/shared-view/Map.vue

@ -0,0 +1,42 @@
<script setup lang="ts">
import {
ActiveViewInj,
FieldsInj,
IsPublicInj,
MetaInj,
ReadonlyInj,
ReloadViewDataHookInj,
useProvideMapViewStore,
} from '#imports'
const { sharedView, meta, sorts, nestedFilters } = useSharedView()
const reloadEventHook = createEventHook()
provide(ReloadViewDataHookInj, reloadEventHook)
provide(ReadonlyInj, ref(true))
provide(MetaInj, meta)
provide(ActiveViewInj, sharedView)
provide(FieldsInj, ref(meta.value?.columns || []))
provide(IsPublicInj, ref(true))
useProvideSmartsheetStore(sharedView, meta, true, sorts, nestedFilters)
useProvideMapViewStore(meta, sharedView, true)
</script>
<template>
<div class="nc-container h-full mt-1.5 px-12">
<div class="flex flex-col h-full flex-1 min-w-0">
<LazySmartsheetToolbar />
<div class="h-full flex-1 min-w-0 min-h-0 bg-gray-50">
<LazySmartsheetMap />
</div>
</div>
</div>
</template>

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

@ -21,6 +21,7 @@ import {
isDuration,
isEmail,
isFloat,
isGeoData,
isInt,
isJSON,
isManualSaved,
@ -100,7 +101,9 @@ const syncValue = useDebounceFn(
)
const vModel = computed({
get: () => props.modelValue,
get: () => {
return props.modelValue
},
set: (val) => {
if (val !== props.modelValue) {
currentRow.value.rowMeta.changed = true
@ -151,6 +154,7 @@ const isNumericField = computed(() => {
>
<template v-if="column">
<LazyCellTextArea v-if="isTextArea(column)" v-model="vModel" />
<LazyCellGeoData v-else-if="isGeoData(column)" v-model="vModel" />
<LazyCellCheckbox v-else-if="isBoolean(column, abstractType)" 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" />

266
packages/nc-gui/components/smartsheet/Map.vue

@ -0,0 +1,266 @@
<script lang="ts" setup>
import 'leaflet/dist/leaflet.css'
import L, { LatLng } from 'leaflet'
import 'leaflet.markercluster'
import { ViewTypes } from 'nocodb-sdk'
import { IsPublicInj, OpenNewRecordFormHookInj, latLongToJoinedString, onMounted, provide, ref } from '#imports'
import type { Row as RowType } from '~/lib'
const route = useRoute()
const router = useRouter()
const reloadViewDataHook = inject(ReloadViewDataHookInj)
const reloadViewMetaHook = inject(ReloadViewMetaHookInj)
const { formattedData, loadMapData, loadMapMeta, mapMetaData, geoDataFieldColumn, addEmptyRow, paginationData } =
useMapViewStoreOrThrow()
const markersClusterGroupRef = ref<L.MarkerClusterGroup>()
const mapContainerRef = ref<HTMLElement>()
const myMapRef = ref<L.Map>()
const isPublic = inject(IsPublicInj, ref(false))
const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref())
const openNewRecordFormHook = inject(OpenNewRecordFormHookInj, createEventHook())
const expandedFormDlg = ref(false)
const expandedFormRow = ref<RowType>()
const expandedFormRowState = ref<Record<string, any>>()
const fallBackCenterLocation = {
lat: 51,
lng: 0.0,
}
const getMapZoomLocalStorageKey = (viewId: string) => {
return `mapView.${viewId}.zoom`
}
const getMapCenterLocalStorageKey = (viewId: string) => `mapView.${viewId}.center`
const expandForm = (row: RowType, state?: Record<string, any>) => {
const rowId = extractPkFromRow(row.row, meta.value!.columns!)
if (rowId) {
router.push({
query: {
...route.query,
rowId,
},
})
} else {
expandedFormRow.value = row
expandedFormRowState.value = state
expandedFormDlg.value = true
}
}
openNewRecordFormHook?.on(async () => {
const newRow = await addEmptyRow()
expandForm(newRow)
})
const expandedFormOnRowIdDlg = computed({
get() {
return !!route.query.rowId
},
set(val) {
if (!val)
router.push({
query: {
...route.query,
rowId: undefined,
},
})
},
})
const addMarker = (lat: number, long: number, row: RowType) => {
if (markersClusterGroupRef.value == null) {
throw new Error('Marker cluster is null')
}
const newMarker = L.marker([lat, long], {
alt: `${lat}, ${long}`,
}).on('click', () => {
expandForm(row)
})
markersClusterGroupRef.value?.addLayer(newMarker)
}
const resetZoomAndCenterBasedOnLocalStorage = () => {
if (mapMetaData?.value?.fk_view_id == null) {
return
}
const initialZoomLevel = parseInt(localStorage.getItem(getMapZoomLocalStorageKey(mapMetaData.value.fk_view_id)) || '10')
const initialCenterLocalStorageStr = localStorage.getItem(getMapCenterLocalStorageKey(mapMetaData.value.fk_view_id))
const initialCenter = initialCenterLocalStorageStr ? JSON.parse(initialCenterLocalStorageStr) : fallBackCenterLocation
myMapRef?.value?.setView([initialCenter.lat, initialCenter.lng], initialZoomLevel)
}
onBeforeMount(async () => {
await loadMapMeta()
await loadMapData()
})
onMounted(async () => {
const myMap = L.map(mapContainerRef.value!, {
center: new LatLng(10, 10),
zoom: 2,
})
myMapRef.value = myMap
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
}).addTo(myMap)
markersClusterGroupRef.value = L.markerClusterGroup({
iconCreateFunction(cluster: { getChildCount: () => number }) {
return L.divIcon({
html: `${cluster.getChildCount()}`,
className: 'bg-pink rounded-full flex items-center justify-center geo-map-marker-cluster',
iconSize: new L.Point(40, 40),
})
},
})
myMap.addLayer(markersClusterGroupRef.value)
myMap.on('zoomend', function () {
if (localStorage != null && mapMetaData?.value?.fk_view_id) {
localStorage.setItem(getMapZoomLocalStorageKey(mapMetaData.value.fk_view_id), myMap.getZoom().toString())
}
})
myMap.on('moveend', function () {
if (localStorage != null && mapMetaData?.value?.fk_view_id) {
localStorage.setItem(getMapCenterLocalStorageKey(mapMetaData?.value?.fk_view_id), JSON.stringify(myMap.getCenter()))
}
})
myMap.on('contextmenu', async function (e) {
const { lat, lng } = e.latlng
const newRow = await addEmptyRow()
if (geoDataFieldColumn.value?.title) {
newRow.row[geoDataFieldColumn.value.title] = latLongToJoinedString(lat, lng)
}
expandForm(newRow)
})
})
reloadViewMetaHook?.on(async () => {
await loadMapMeta()
})
reloadViewDataHook?.on(async () => {
await loadMapData()
})
provide(ReloadRowDataHookInj, reloadViewDataHook)
watch([formattedData, mapMetaData, markersClusterGroupRef], () => {
if (formattedData.value == null || mapMetaData.value?.fk_view_id == null || markersClusterGroupRef.value == null) {
return
}
resetZoomAndCenterBasedOnLocalStorage()
markersClusterGroupRef.value?.clearLayers()
formattedData.value?.forEach((row) => {
const primaryGeoDataColumnTitle = geoDataFieldColumn.value?.title
if (primaryGeoDataColumnTitle == null) {
throw new Error('Cannot find primary geo data column title')
}
const primaryGeoDataValue = row.row[primaryGeoDataColumnTitle]
if (primaryGeoDataValue == null) {
return
}
const [lat, long] = primaryGeoDataValue.split(';').map(parseFloat)
addMarker(lat, long, row)
})
})
watch(view, async (nextView) => {
if (nextView?.type === ViewTypes.MAP) {
await loadMapMeta()
await loadMapData()
}
})
const count = computed(() => paginationData.value.totalRows)
</script>
<template>
<div class="flex flex-col h-full w-full no-underline" data-testid="nc-map-wrapper">
<div id="mapContainer" ref="mapContainerRef" class="w-full h-screen">
<a-tooltip placement="bottom" class="h-2 w-auto max-w-fit-content absolute top-3 right-3 p-2 z-500 cursor-default">
<template #title>
<span v-if="count > 1000"> {{ $t('msg.info.map.overLimit') }} </span>
<span v-else-if="count > 900"> {{ $t('msg.info.map.closeLimit') }} </span>
<span> {{ $t('msg.info.map.limitNumber') }} </span>
</template>
<div v-if="count > 900" class="nc-warning-info flex min-w-32px h-32px items-center gap-1 px-2 bg-white">
<div>{{ count }} {{ $t('objects.records') }}</div>
<mdi-map-marker-alert />
</div>
</a-tooltip>
</div>
</div>
<Suspense v-if="!isPublic">
<LazySmartsheetExpandedForm
v-if="expandedFormRow && expandedFormDlg"
v-model="expandedFormDlg"
:row="expandedFormRow"
:state="expandedFormRowState"
:meta="meta"
:view="view"
/>
</Suspense>
<Suspense v-if="!isPublic">
<LazySmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg"
:key="route.query.rowId"
v-model="expandedFormOnRowIdDlg"
:row="{ row: {}, oldRow: {}, rowMeta: {} }"
:meta="meta"
:row-id="route.query.rowId"
:view="view"
/>
</Suspense>
</template>
<style scoped lang="scss">
:global(.geo-map-marker-cluster) {
background-color: pink;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
</style>
<style>
.no-underline a {
text-decoration: none !important;
}
.leaflet-popup-content-wrapper {
max-height: 255px;
overflow: scroll;
}
</style>

12
packages/nc-gui/components/smartsheet/Toolbar.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import { IsPublicInj, inject, ref, useSharedView, useSidebar, useSmartsheetStoreOrThrow, useUIPermission } from '#imports'
const { isGrid, isForm, isGallery, isKanban, isSqlView } = useSmartsheetStoreOrThrow()
const { isGrid, isForm, isGallery, isKanban, isMap, isSqlView } = useSmartsheetStoreOrThrow()
const isPublic = inject(IsPublicInj, ref(false))
@ -21,7 +21,7 @@ const { allowCSVDownload } = useSharedView()
style="z-index: 7"
>
<LazySmartsheetToolbarViewActions
v-if="(isGrid || isGallery || isKanban) && !isPublic && isUIAllowed('dataInsert')"
v-if="(isGrid || isGallery || isKanban || isMap) && !isPublic && isUIAllowed('dataInsert')"
:show-system-fields="false"
class="ml-1"
/>
@ -32,15 +32,17 @@ const { allowCSVDownload } = useSharedView()
<LazySmartsheetToolbarKanbanStackEditOrAdd v-if="isKanban" />
<LazySmartsheetToolbarFieldsMenu v-if="isGrid || isGallery || isKanban" :show-system-fields="false" />
<LazySmartsheetToolbarMappedBy v-if="isMap" />
<LazySmartsheetToolbarColumnFilterMenu v-if="isGrid || isGallery || isKanban" />
<LazySmartsheetToolbarFieldsMenu v-if="isGrid || isGallery || isKanban || isMap" :show-system-fields="false" />
<LazySmartsheetToolbarColumnFilterMenu v-if="isGrid || isGallery || isKanban || isMap" />
<LazySmartsheetToolbarSortListMenu v-if="isGrid || isGallery || isKanban" />
<LazySmartsheetToolbarRowHeight v-if="isGrid" />
<LazySmartsheetToolbarShareView v-if="(isForm || isGrid || isKanban || isGallery) && !isPublic" />
<LazySmartsheetToolbarShareView v-if="(isForm || isGrid || isKanban || isGallery || isMap) && !isPublic" />
<LazySmartsheetToolbarQrScannerButton v-if="isGrid || isKanban || isGallery" />

13
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue

@ -57,9 +57,13 @@ const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
const onlyNameUpdateOnEditColumns = [UITypes.LinkToAnotherRecord, UITypes.Lookup, UITypes.Rollup]
const geoDataToggleCondition = (t) => {
return geodataToggleState.show ? geodataToggleState.show : !t.name.includes(UITypes.GeoData)
}
const uiTypesOptions = computed<typeof uiTypes>(() => {
return [
...uiTypes.filter((t) => !isEdit.value || !t.virtual),
...uiTypes.filter((t) => geoDataToggleCondition(t) && (!isEdit.value || !t.virtual)),
...(!isEdit.value && meta?.value?.columns?.every((c) => !c.pk)
? [
{
@ -80,8 +84,12 @@ const reloadMetaAndData = async () => {
}
}
const saving = ref(false)
async function onSubmit() {
saving.value = true
const saved = await addOrUpdate(reloadMetaAndData, props.columnPosition)
saving.value = false
if (!saved) return
@ -178,6 +186,7 @@ useEventListener('keydown', (e: KeyboardEvent) => {
<LazySmartsheetColumnQrCodeOptions v-if="formState.uidt === UITypes.QrCode" v-model="formState" />
<LazySmartsheetColumnBarcodeOptions v-if="formState.uidt === UITypes.Barcode" v-model="formState" />
<LazySmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" v-model:value="formState" />
<LazySmartsheetColumnGeoDataOptions v-if="formState.uidt === UITypes.GeoData" v-model:value="formState" />
<LazySmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" v-model:value="formState" />
<LazySmartsheetColumnRatingOptions v-if="formState.uidt === UITypes.Rating" v-model:value="formState" />
<LazySmartsheetColumnCheckboxOptions v-if="formState.uidt === UITypes.Checkbox" v-model:value="formState" />
@ -234,7 +243,7 @@ useEventListener('keydown', (e: KeyboardEvent) => {
{{ $t('general.cancel') }}
</a-button>
<a-button html-type="submit" type="primary" @click.prevent="onSubmit">
<a-button html-type="submit" type="primary" :loading="saving" @click.prevent="onSubmit">
<!-- Save -->
{{ $t('general.save') }}
</a-button>

9
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -3,6 +3,7 @@ import type { TableType, ViewType } from 'nocodb-sdk'
import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import type { Ref } from 'vue'
import {
CellClickHookInj,
FieldsInj,
IsFormInj,
IsKanbanInj,
@ -49,6 +50,9 @@ const meta = toRef(props, 'meta')
const router = useRouter()
// override cell click hook to avoid unexpected behavior at form fields
provide(CellClickHookInj, null)
const fields = computedInject(FieldsInj, (_fields) => {
if (props.useMetaFields) {
return (meta.value.columns ?? []).filter((col) => !isSystemColumn(col))
@ -132,7 +136,6 @@ reloadHook.on(() => {
if (isNew.value) return
loadRow()
})
provide(ReloadRowDataHookInj, reloadHook)
if (isKanban.value) {
@ -147,9 +150,7 @@ if (isKanban.value) {
const cellWrapperEl = ref<HTMLElement>()
onMounted(() => {
setTimeout(() => {
;(cellWrapperEl.value?.querySelector('input,select,textarea') as HTMLInputElement)?.focus()
})
setTimeout(() => (cellWrapperEl.value?.querySelector('input,select,textarea') as HTMLInputElement)?.focus())
})
</script>

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

@ -15,6 +15,7 @@ import {
isDuration,
isEmail,
isFloat,
isGeoData,
isInt,
isJSON,
isPercent,
@ -44,6 +45,7 @@ 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 GeoDataIcon from '~icons/mdi/map-marker'
import RatingIcon from '~icons/mdi/star'
import GenericIcon from '~icons/mdi/square-rounded'
import NumericIcon from '~icons/mdi/numeric'
@ -64,6 +66,8 @@ const renderIcon = (column: ColumnType, abstractType: any) => {
return CalendarIcon
} else if (isDateTime(column, abstractType)) {
return DatetimeIcon
} else if (isGeoData(column)) {
return GeoDataIcon
} else if (isSet(column)) {
return MultiSelectIcon
} else if (isSingleSelect(column)) {

216
packages/nc-gui/components/smartsheet/sidebar/MenuBottom.vue

@ -2,12 +2,12 @@
import { ViewTypes } from 'nocodb-sdk'
import { useNuxtApp, useSmartsheetStoreOrThrow, viewIcons } from '#imports'
const emits = defineEmits<Emits>()
interface Emits {
(event: 'openModal', data: { type: ViewTypes; title?: string }): void
}
const emits = defineEmits<Emits>()
const { $e } = useNuxtApp()
const { isSqlView } = useSmartsheetStoreOrThrow()
@ -20,101 +20,121 @@ function onOpenModal(type: ViewTypes, title = '') {
<template>
<a-menu :selected-keys="[]" class="flex flex-col">
<div>
<h3 class="px-3 text-xs font-semibold flex items-center gap-4 text-gray-500">
{{ $t('activity.createView') }}
</h3>
<a-menu-item
key="grid"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-grid-view"
@click="onOpenModal(ViewTypes.GRID)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.grid') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GRID].icon" :style="{ color: viewIcons[ViewTypes.GRID].color }" />
<div>{{ $t('objects.viewType.grid') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
key="gallery"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-gallery-view"
@click="onOpenModal(ViewTypes.GALLERY)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.gallery') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GALLERY].icon" :style="{ color: viewIcons[ViewTypes.GALLERY].color }" />
<div>{{ $t('objects.viewType.gallery') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
v-if="!isSqlView"
key="form"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-form-view"
@click="onOpenModal(ViewTypes.FORM)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.form') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.FORM].icon" :style="{ color: viewIcons[ViewTypes.FORM].color }" />
<div>{{ $t('objects.viewType.form') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
key="kanban"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-kanban-view"
@click="onOpenModal(ViewTypes.KANBAN)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.kanban') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.KANBAN].icon" :style="{ color: viewIcons[ViewTypes.KANBAN].color }" />
<div>{{ $t('objects.viewType.kanban') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<div class="w-full h-4" />
</div>
<h3 class="px-3 text-xs font-semibold flex items-center gap-4 text-gray-500">
{{ $t('activity.createView') }}
</h3>
<a-menu-item
key="grid"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-grid-view"
@click="onOpenModal(ViewTypes.GRID)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.grid') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GRID].icon" :style="{ color: viewIcons[ViewTypes.GRID].color }" />
<div>{{ $t('objects.viewType.grid') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
key="gallery"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-gallery-view"
@click="onOpenModal(ViewTypes.GALLERY)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.gallery') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GALLERY].icon" :style="{ color: viewIcons[ViewTypes.GALLERY].color }" />
<div>{{ $t('objects.viewType.gallery') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
v-if="!isSqlView"
key="form"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-form-view"
@click="onOpenModal(ViewTypes.FORM)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.form') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.FORM].icon" :style="{ color: viewIcons[ViewTypes.FORM].color }" />
<div>{{ $t('objects.viewType.form') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
key="kanban"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-kanban-view"
@click="onOpenModal(ViewTypes.KANBAN)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.kanban') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.KANBAN].icon" :style="{ color: viewIcons[ViewTypes.KANBAN].color }" />
<div>{{ $t('objects.viewType.kanban') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<a-menu-item
v-if="geodataToggleState.show"
key="map"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-map-view"
@click="onOpenModal(ViewTypes.MAP)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
<template #title>
{{ $t('msg.info.addView.map') }}
</template>
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.MAP].icon" :style="{ color: viewIcons[ViewTypes.MAP].color }" />
<div>{{ $t('objects.viewType.map') }}</div>
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
<div class="w-full h-4" />
</a-menu>
</template>

15
packages/nc-gui/components/smartsheet/sidebar/toolbar/GeodataSwitcher.vue

@ -0,0 +1,15 @@
<script setup lang="ts">
function toggleGeodataFeature() {
geodataToggleState.show = !geodataToggleState.show
localStorage.setItem('geodataToggleState', JSON.stringify(geodataToggleState.show))
}
</script>
<template>
<a-tooltip placement="bottomRight">
<template #title>
<span> Toggle GeoData </span>
</template>
<mdi-map-marker class="cursor-pointer" data-testid="toggle-geodata-feature-icon" @click="toggleGeodataFeature" />
</a-tooltip>
</template>

4
packages/nc-gui/components/smartsheet/sidebar/toolbar/index.vue

@ -29,6 +29,10 @@ const onClick = () => {
<LazySmartsheetSidebarToolbarDebugMeta />
<div class="dot" />
<LazySmartsheetSidebarToolbarGeodataSwitcher />
<div class="dot" />
</template>
<slot name="end" />

3
packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue

@ -57,6 +57,8 @@ const { eventBus } = useSmartsheetStoreOrThrow()
eventBus.on((event) => {
if (event === SmartsheetStoreEvents.FIELD_RELOAD) {
loadViewColumns()
} else if (event === SmartsheetStoreEvents.MAPPED_BY_COLUMN_CHANGE) {
loadViewColumns()
}
})
@ -206,6 +208,7 @@ useMenuCloseOnEsc(open)
v-model:checked="field.show"
v-e="['a:fields:show-hide']"
class="shrink"
:disabled="field.isViewEssentialField"
@change="saveOrUpdate(field, index)"
>
<div class="flex items-center">

113
packages/nc-gui/components/smartsheet/toolbar/MappedBy.vue

@ -0,0 +1,113 @@
<script setup lang="ts">
import type { MapType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import type { SelectProps } from 'ant-design-vue'
import {
ActiveViewInj,
IsLockedInj,
MetaInj,
ReloadViewDataHookInj,
computed,
inject,
ref,
useViewColumns,
watch,
} from '#imports'
const { eventBus } = useSmartsheetStoreOrThrow()
const meta = inject(MetaInj, ref())
const activeView = inject(ActiveViewInj, ref())
const reloadDataHook = inject(ReloadViewDataHookInj)!
const isLocked = inject(IsLockedInj, ref(false))
const { fields, loadViewColumns, metaColumnById } = useViewColumns(activeView, meta, () => reloadDataHook.trigger())
const { loadMapData, loadMapMeta, updateMapMeta, mapMetaData, geoDataFieldColumn } = useMapViewStoreOrThrow()
const mappedByDropdown = ref(false)
watch(
() => activeView.value?.id,
async (newVal, oldVal) => {
if (newVal !== oldVal && meta.value) {
await loadViewColumns()
}
},
{ immediate: true },
)
const geoDataMappingFieldColumnId = computed({
get: () => mapMetaData.value.fk_geo_data_col_id,
set: async (val) => {
if (val) {
await updateMapMeta({
fk_geo_data_col_id: val,
})
await loadMapMeta()
await loadMapData()
;(activeView.value?.view as MapType).fk_geo_data_col_id = val
eventBus.emit(SmartsheetStoreEvents.MAPPED_BY_COLUMN_CHANGE)
}
},
})
const geoDataFieldOptions = computed<SelectProps['options']>(() => {
return fields.value
?.filter((el) => el.fk_column_id && metaColumnById.value[el.fk_column_id].uidt === UITypes.GeoData)
.map((field) => {
return {
value: field.fk_column_id,
label: field.title,
}
})
})
const handleChange = () => {
mappedByDropdown.value = false
}
</script>
<template>
<a-dropdown v-model:visible="mappedByDropdown" :trigger="['click']">
<div class="nc-map-btn">
<a-button v-e="['c:map:change-grouping-field']" class="nc-map-stacked-by-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-1">
<mdi-arrow-down-drop-circle-outline />
<span class="text-capitalize !text-sm font-weight-normal">
{{ $t('activity.map.mappedBy') }}
<span class="font-bold">{{ geoDataFieldColumn?.title }}</span>
</span>
<MdiMenuDown class="text-grey" />
</div>
</a-button>
</div>
<template #overlay>
<div
v-if="mappedByDropdown"
class="p-3 min-w-[280px] bg-gray-50 shadow-lg nc-table-toolbar-menu max-h-[max(80vh,500px)] overflow-auto !border"
@click.stop
>
<div>
<span class="font-bold"> {{ $t('activity.map.chooseMappingField') }}</span>
<a-divider class="!my-2" />
</div>
<div class="nc-fields-list py-1">
<div class="grouping-field">
<a-select
v-model:value="geoDataMappingFieldColumnId"
class="w-full nc-msp-grouping-field-select"
:options="geoDataFieldOptions"
placeholder="Select a Mapping Field"
@change="handleChange"
@click.stop
/>
</div>
</div>
</div>
</template>
</a-dropdown>
</template>

9
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -118,6 +118,9 @@ const sharedViewUrl = computed(() => {
case ViewTypes.GALLERY:
viewType = 'gallery'
break
case ViewTypes.MAP:
viewType = 'map'
break
default:
viewType = 'view'
}
@ -352,7 +355,11 @@ const copyIframeCode = async () => {
<div
v-if="
shared && (shared.type === ViewTypes.GRID || shared.type === ViewTypes.KANBAN || shared.type === ViewTypes.GALLERY)
shared &&
(shared.type === ViewTypes.GRID ||
shared.type === ViewTypes.KANBAN ||
shared.type === ViewTypes.GALLERY ||
shared.type === ViewTypes.MAP)
"
>
<!-- Allow Download -->

3
packages/nc-gui/components/smartsheet/toolbar/SharedViewList.vue

@ -56,6 +56,9 @@ const sharedViewUrl = (view: SharedViewType) => {
case ViewTypes.FORM:
viewType = 'form'
break
case ViewTypes.MAP:
viewType = 'map'
break
case ViewTypes.KANBAN:
viewType = 'kanban'
break

55
packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue

@ -1,8 +1,10 @@
<script lang="ts" setup>
import type { Ref } from '@vue/reactivity'
import {
ActiveViewInj,
IsLockedInj,
IsPublicInj,
MetaInj,
extractSdkResponseErrorMsg,
inject,
message,
@ -43,12 +45,27 @@ const showApiSnippetDrawer = ref(false)
const showErd = ref(false)
const quickImportDialog = ref(false)
type QuickImportDialogType = 'csv' | 'excel' | 'json'
// TODO: add 'json' when it's ready
const quickImportDialogTypes: QuickImportDialogType[] = ['csv', 'excel']
const quickImportDialogs: Record<typeof quickImportDialogTypes[number], Ref<boolean>> = quickImportDialogTypes.reduce(
(acc: any, curr) => {
acc[curr] = ref(false)
return acc
},
{},
) as Record<QuickImportDialogType, Ref<boolean>>
const { isUIAllowed } = useUIPermission()
const { isSharedBase } = useProject()
const meta = inject(MetaInj, ref())
const currentBaseId = computed(() => meta.value?.base_id)
const Icon = computed(() => {
switch (selectedView.value?.lock_type) {
case LockType.Personal:
@ -175,19 +192,20 @@ useMenuCloseOnEsc(open)
</template>
<template #expandIcon></template>
<a-menu-item v-if="isUIAllowed('csvImport') && !isView && !isPublicView">
<div
v-e="['a:actions:upload-csv']"
class="nc-project-menu-item"
:class="{ disabled: isLocked }"
@click="!isLocked ? (quickImportDialog = true) : {}"
>
<MdiUploadOutline class="text-gray-500" />
<!-- Upload CSV -->
{{ $t('activity.uploadCSV') }}
<div class="flex items-center text-gray-400"><MdiAlpha />version</div>
</div>
</a-menu-item>
<template v-for="(dialog, type) in quickImportDialogs">
<a-menu-item v-if="isUIAllowed(`${type}Import`) && !isView && !isPublicView" :key="type">
<div
v-e="[`a:actions:upload-${type}`]"
class="nc-project-menu-item"
:class="{ disabled: isLocked }"
@click="!isLocked ? (dialog.value = true) : {}"
>
<MdiUploadOutline class="text-gray-500" />
{{ `${$t('general.upload')} ${type.toUpperCase()}` }}
<div class="flex items-center text-gray-400"><MdiAlpha />version</div>
</div>
</a-menu-item>
</template>
</a-sub-menu>
</template>
@ -232,7 +250,14 @@ useMenuCloseOnEsc(open)
</template>
</a-dropdown>
<LazyDlgQuickImport v-if="quickImportDialog" v-model="quickImportDialog" import-type="csv" :import-data-only="true" />
<LazyDlgQuickImport
v-for="type in quickImportDialogTypes"
:key="type"
v-model="quickImportDialogs[type].value"
:import-type="type"
:base-id="currentBaseId"
:import-data-only="true"
/>
<LazyWebhookDrawer v-if="showWebhookDrawer" v-model="showWebhookDrawer" />

5
packages/nc-gui/components/tabs/Smartsheet.vue

@ -39,7 +39,7 @@ const fields = ref<ColumnType[]>([])
const meta = computed<TableType | undefined>(() => activeTab.value && metas.value[activeTab.value.id!])
const { isGallery, isGrid, isForm, isKanban, isLocked } = useProvideSmartsheetStore(activeView, meta)
const { isGallery, isGrid, isForm, isKanban, isLocked, isMap } = useProvideSmartsheetStore(activeView, meta)
const reloadEventHook = createEventHook<void | boolean>()
@ -48,6 +48,7 @@ const reloadViewMetaEventHook = createEventHook<void | boolean>()
const openNewRecordFormHook = createEventHook<void>()
useProvideKanbanViewStore(meta, activeView)
useProvideMapViewStore(meta, activeView)
// todo: move to store
provide(MetaInj, meta)
@ -81,6 +82,8 @@ provide(
<LazySmartsheetForm v-else-if="isForm && !$route.query.reload" />
<LazySmartsheetKanban v-else-if="isKanban" />
<LazySmartsheetMap v-else-if="isMap" />
</div>
</div>
</template>

16
packages/nc-gui/components/template/Editor.vue

@ -405,10 +405,14 @@ async function importTemplate() {
const tableId = meta.value?.id
const projectName = project.value.title!
const table_names = data.tables.map((t: Record<string, any>) => t.table_name)
await Promise.all(
Object.keys(importData).map((key: string) =>
(async (k) => {
if (!table_names.includes(k)) {
return
}
const data = importData[k]
const total = data.length
@ -458,7 +462,7 @@ async function importTemplate() {
// Successfully imported table data
message.success(t('msg.success.tableDataImported'))
} catch (e: any) {
message.error(e.message)
message.error(await extractSdkResponseErrorMsg(e))
} finally {
isImporting.value = false
}
@ -633,6 +637,16 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
</span>
</template>
<template #extra>
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Delete Table</span>
</template>
<mdi-delete-outline v-if="data.tables.length > 1" class="text-lg mr-8" @click.stop="deleteTable(tableIdx)" />
</a-tooltip>
</template>
<a-table
v-if="srcDestMapping"
class="template-form"

35
packages/nc-gui/components/virtual-cell/components/ListItems.vue

@ -48,14 +48,20 @@ const saveRow = inject(SaveRowInj, () => {})
const selectedRowIndex = ref(0)
const isAltKeyDown = ref(false)
const linkRow = async (row: Record<string, any>) => {
if (isNew.value) {
addLTARRef(row, column?.value as ColumnType)
saveRow()
saveRow!()
} else {
await link(row)
}
vModel.value = false
if (isAltKeyDown.value) {
loadChildrenExcludedList()
} else {
vModel.value = false
}
}
/** reload list on modal open */
@ -159,6 +165,27 @@ useSelectedCellKeyupListener(vModel, (e: KeyboardEvent) => {
const activeRow = (vNode?: InstanceType<typeof Card>) => {
vNode?.$el?.scrollIntoView({ block: 'nearest', inline: 'nearest' })
}
// set variable to true when alt key is pressed
const keyDownHandler = (e: KeyboardEvent) => {
isAltKeyDown.value = e.altKey
}
// set variable to false when key is released
const keyUpHandler = (e: KeyboardEvent) => {
isAltKeyDown.value = e.altKey
}
// add event listeners when vModel is true and remove when false
watch(vModel, (nextVal) => {
if (nextVal) {
document.addEventListener('keydown', keyDownHandler)
document.addEventListener('keyup', keyUpHandler)
} else {
document.removeEventListener('keydown', keyDownHandler)
document.removeEventListener('keyup', keyUpHandler)
}
})
</script>
<template>
@ -219,6 +246,10 @@ const activeRow = (vNode?: InstanceType<typeof Card>) => {
show-less-items
/>
</div>
<div class="text-xs text-gray-400 text-center px-2 mt-4 pb-0">
* Use <kbd>ALT</kbd> / <kbd>OPT</kbd> + <kbd>Click</kbd> to select multiple records
</div>
</template>
<a-empty v-else class="my-10" :image="Empty.PRESENTED_IMAGE_SIMPLE" />

7
packages/nc-gui/composables/useAttachment.ts

@ -1,4 +1,4 @@
import { mimeTypes, openLink, useGlobal } from '#imports'
import { openLink, useGlobal } from '#imports'
const useAttachment = () => {
const { appInfo } = useGlobal()
@ -15,11 +15,10 @@ const useAttachment = () => {
return item.data
}
const sources = getPossibleAttachmentSrc(item)
const mimeType = mimeTypes[item?.mimetype?.split('/')?.pop() || 'txt']
for (const source of sources) {
// test if the source is accessible or not
const res = await fetch(source)
if (res.ok && res.headers.get('Content-Type') === mimeType) {
const res = await fetch(source, { method: 'HEAD' })
if (res.ok) {
return source
}
}

182
packages/nc-gui/composables/useMapViewDataStore.ts

@ -0,0 +1,182 @@
import { reactive } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import type { ColumnType, MapType, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
import { IsPublicInj, ref, useInjectionState, useMetas, useProject } from '#imports'
import type { Row } from '~/lib'
const storedValue = localStorage.getItem('geodataToggleState')
const initialState = storedValue ? JSON.parse(storedValue) : false
export const geodataToggleState = reactive({ show: initialState })
const formatData = (list: Record<string, any>[]) =>
list.map(
(row) =>
({
row: { ...row },
oldRow: { ...row },
rowMeta: {},
} as Row),
)
const [useProvideMapViewStore, useMapViewStore] = useInjectionState(
(
meta: Ref<TableType | undefined>,
viewMeta: Ref<ViewType | MapType | undefined> | ComputedRef<(ViewType & { id: string }) | undefined>,
shared = false,
where?: ComputedRef<string | undefined>,
) => {
if (!meta) {
throw new Error('Table meta is not available')
}
const formattedData = ref<Row[]>([])
const { api } = useApi()
const { project } = useProject()
const { $api } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const isPublic = ref(shared) || inject(IsPublicInj, ref(false))
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const { fetchSharedViewData } = useSharedView()
const mapMetaData = ref<MapType>({})
const geoDataFieldColumn = ref<ColumnType | undefined>()
const defaultPageSize = 1000
const paginationData = ref<PaginatedType>({ page: 1, pageSize: defaultPageSize })
const queryParams = computed(() => ({
limit: paginationData.value.pageSize ?? defaultPageSize,
where: where?.value ?? '',
}))
async function syncCount() {
const { count } = await $api.dbViewRow.count(
NOCO,
project?.value?.title as string,
meta?.value?.id as string,
viewMeta?.value?.id as string,
)
paginationData.value.totalRows = count
}
async function loadMapMeta() {
if (!viewMeta?.value?.id || !meta?.value?.columns) return
mapMetaData.value = await $api.dbView.mapRead(viewMeta.value.id)
geoDataFieldColumn.value =
(meta.value.columns as ColumnType[]).filter((f) => f.id === mapMetaData.value.fk_geo_data_col_id)[0] || {}
}
async function loadMapData() {
if ((!project?.value?.id || !meta.value?.id || !viewMeta.value?.id) && !isPublic?.value) return
const res = !isPublic.value
? await api.dbViewRow.list('noco', project.value.id!, meta.value!.id!, viewMeta.value!.id!, {
...queryParams.value,
...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(nestedFilters.value) }),
where: where?.value,
})
: await fetchSharedViewData({ sortsArr: sorts.value, filtersArr: nestedFilters.value })
formattedData.value = formatData(res.list)
}
async function updateMapMeta(updateObj: Partial<MapType>) {
if (!viewMeta?.value?.id || !isUIAllowed('xcDatatableEditable')) return
await $api.dbView.mapUpdate(viewMeta.value.id, {
...mapMetaData.value,
...updateObj,
})
}
const { getMeta } = useMetas()
async function insertRow(
currentRow: Row,
ltarState: Record<string, any> = {},
{
metaValue = meta.value,
viewMetaValue = viewMeta.value,
}: { metaValue?: MapType; viewMetaValue?: ViewType | MapType } = {},
) {
const row = currentRow.row
if (currentRow.rowMeta) currentRow.rowMeta.saving = true
try {
const { missingRequiredColumns, insertObj } = await populateInsertObject({
meta: metaValue!,
ltarState,
getMeta,
row,
})
if (missingRequiredColumns.size) return
const insertedData = await $api.dbViewRow.create(
NOCO,
project?.value.id as string,
metaValue?.id as string,
viewMetaValue?.id as string,
insertObj,
)
Object.assign(currentRow, {
row: { ...insertedData, ...row },
rowMeta: { ...(currentRow.rowMeta || {}), new: undefined },
oldRow: { ...insertedData },
})
syncCount()
return insertedData
} catch (error: any) {
message.error(await extractSdkResponseErrorMsg(error))
} finally {
if (currentRow.rowMeta) currentRow.rowMeta.saving = false
}
}
function addEmptyRow(addAfter = formattedData.value.length) {
formattedData.value.splice(addAfter, 0, {
row: {},
oldRow: {},
rowMeta: { new: true },
})
return formattedData.value[addAfter]
}
return {
formattedData,
loadMapData,
loadMapMeta,
updateMapMeta,
mapMetaData,
geoDataFieldColumn,
addEmptyRow,
insertRow,
geodataToggleState,
syncCount,
paginationData,
}
},
)
export { useProvideMapViewStore }
export function useMapViewStoreOrThrow() {
const mapViewStore = useMapViewStore()
if (mapViewStore == null) throw new Error('Please call `useProvideMapViewStore` on the appropriate parent component')
return mapViewStore
}

2
packages/nc-gui/composables/useMultiSelect/index.ts

@ -145,14 +145,12 @@ export function useMultiSelect(
return
}
editEnabled.value = false
isMouseDown = true
selectedRange.startRange({ row, col })
}
const handleCellClick = (event: MouseEvent, row: number, col: number) => {
isMouseDown = true
editEnabled.value = false
selectedRange.startRange({ row, col })
selectedRange.endRange({ row, col })
makeActive(row, col)

3
packages/nc-gui/composables/useSmartsheetStore.ts

@ -26,13 +26,13 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
const eventBus = useEventBus<SmartsheetStoreEvents>(Symbol('SmartsheetStore'))
// getters
const isLocked = computed(() => view.value?.lock_type === 'locked')
const isPkAvail = computed(() => (meta.value as TableType)?.columns?.some((c) => c.pk))
const isGrid = computed(() => view.value?.type === ViewTypes.GRID)
const isForm = computed(() => view.value?.type === ViewTypes.FORM)
const isGallery = computed(() => view.value?.type === ViewTypes.GALLERY)
const isKanban = computed(() => view.value?.type === ViewTypes.KANBAN)
const isMap = computed(() => view.value?.type === ViewTypes.MAP)
const isSharedForm = computed(() => isForm.value && shared)
const xWhere = computed(() => {
let where
@ -65,6 +65,7 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
isGrid,
isGallery,
isKanban,
isMap,
cellRefs,
isSharedForm,
sorts,

15
packages/nc-gui/composables/useViewColumns.ts

@ -1,5 +1,5 @@
import { isSystemColumn } from 'nocodb-sdk'
import type { ColumnType, TableType, ViewType } from 'nocodb-sdk'
import { ViewTypes, isSystemColumn } from 'nocodb-sdk'
import type { ColumnType, MapType, TableType, ViewType } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import { IsPublicInj, computed, inject, ref, useNuxtApp, useProject, useUIPermission, watch } from '#imports'
import type { Field } from '~/lib'
@ -25,6 +25,13 @@ export function useViewColumns(
() => isPublic.value || !isUIAllowed('hideAllColumns') || !isUIAllowed('showAllColumns') || isSharedBase.value,
)
const isColumnViewEssential = (column: ColumnType) => {
// TODO: consider at some point ti delegate this via a cleaner design pattern to view specific check logic
// which could be inside of a view specific helper class (and generalized via an interface)
// (on the other hand, the logic complexity is still very low atm - might be overkill)
return view.value?.type === ViewTypes.MAP && (view.value?.view as MapType)?.fk_geo_data_col_id === column.id
}
const metaColumnById = computed<Record<string, ColumnType>>(() => {
if (!meta.value?.columns) return {}
@ -62,8 +69,10 @@ export function useViewColumns(
title: column.title,
fk_column_id: column.id,
...currentColumnField,
show: currentColumnField.show || isColumnViewEssential(currentColumnField),
order: currentColumnField.order || order++,
system: isSystemColumn(metaColumnById?.value?.[currentColumnField.fk_column_id!]),
isViewEssentialField: isColumnViewEssential(column),
}
})
.sort((a: Field, b: Field) => a.order - b.order)
@ -98,7 +107,7 @@ export function useViewColumns(
if (isLocalMode.value) {
fields.value = fields.value?.map((field: Field) => ({
...field,
show: false,
show: !!field.isViewEssentialField,
}))
reloadData?.()
return

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "مشروع",
@ -98,7 +99,8 @@
"gallery": "معرض صور",
"form": "نموذج",
"kanban": "كانبان",
"calendar": "تقويم"
"calendar": "تقويم",
"map": "Map"
},
"user": "مستخدم",
"users": "مستخدمين",
@ -136,6 +138,7 @@
"Currency": "عملة",
"Percent": "نسبة مؤية",
"Duration": "مدة",
"GeoData": "GeoData",
"Rating": "تقييم",
"Formula": "معادلة",
"Rollup": "جمع البيانات",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "وظيفة التجميع",
"dbCreateIfNotExists": "قاعدة البيانات: إنشاء إذا لم يكن موجودا",
"clientKey": "مفتاح العميل",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "الصفوف لكل صفحة",
"upload": "حدد الملف المراد رفعه",
"upload_sub": "أو سحب وإسقاط الملف",
@ -600,6 +615,7 @@
"gallery": "إضافة عرض المعرض",
"form": "إضافة عرض النموذج",
"kanban": "إضافة عرض كانبان",
"map": "Add Map View",
"calendar": "إضافة طريقة عرض التقويم"
},
"tablesMetadataInSync": "تزامن البيانات الوصفية للجداول",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "পরকলপ",
@ -98,7 +99,8 @@
"gallery": "গ",
"form": "ফরম",
"kanban": "কনবন",
"calendar": "কর"
"calendar": "কর",
"map": "Map"
},
"user": "বযবহরক",
"users": "বযবহরক",
@ -136,6 +138,7 @@
"Currency": "ম",
"Percent": "শতশ",
"Duration": "সমযল",
"GeoData": "GeoData",
"Rating": "রি",
"Formula": "সর",
"Rollup": "রলআপ",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "সমগিক ফশন",
"dbCreateIfNotExists": "ডস: উপসিত নকলি করন",
"clientKey": "কট ক",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "পরতিি",
"upload": "আপলড করতইল নিচন করন",
"upload_sub": "অথবইল ট আনন",
@ -600,6 +615,7 @@
"gallery": "গিউ যত করন",
"form": "ফরম ভিউ যগ করন",
"kanban": "কনবন ভিউ যত করন",
"map": "Add Map View",
"calendar": "কর ভিউ যত করন"
},
"tablesMetadataInSync": "টির মিক কর আছ",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

21
packages/nc-gui/lang/cs.json

@ -74,7 +74,8 @@
"insertBefore": "Vložit před",
"hideField": "Skrýt pole",
"sortAsc": "Seřadit vzestupně",
"sortDesc": "Seřadit sestupně"
"sortDesc": "Seřadit sestupně",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galerie",
"form": "Formulář",
"kanban": "Kanban",
"calendar": "Kalendář"
"calendar": "Kalendář",
"map": "Map"
},
"user": "Uživatel",
"users": "Uživatelé",
@ -136,6 +138,7 @@
"Currency": "Měna",
"Percent": "Procenta",
"Duration": "Doba trvání",
"GeoData": "GeoData",
"Rating": "Hodnocení",
"Formula": "Vzorec",
"Rollup": "Rollup",
@ -253,6 +256,9 @@
"barcodeFormat": "Formát čárového kódu",
"qrCodeValueTooLong": "Příliš mnoho znaků pro QR kód",
"barcodeValueTooLong": "Příliš mnoho znaků pro čárový kód",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Agregační funkce",
"dbCreateIfNotExists": "Databáze : vytvořit, pokud neexistuje",
"clientKey": "Klíč klienta",
@ -452,6 +458,10 @@
"stackedBy": "Naskládáno podle",
"chooseGroupingField": "Výběr pole pro seskupení",
"addOrEditStack": "Přidat / upravit zásobník"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Tvůrce může vytvářet nové projekty a přistupovat k jakémukoli pozvanému projektu.",
"orgViewer": "Prohlížeč nemůže vytvářet nové projekty, ale může přistupovat k jakémukoli pozvanému projektu."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Řádky na stránku",
"upload": "Vyberte soubor, který chcete nahrát",
"upload_sub": "nebo přetažením souboru",
@ -600,6 +615,7 @@
"gallery": "Přidat zobrazení galerie",
"form": "Přidání zobrazení formuláře",
"kanban": "Přidání zobrazení Kanban",
"map": "Add Map View",
"calendar": "Přidat zobrazení kalendáře"
},
"tablesMetadataInSync": "Metadata tabulek jsou synchronizována",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Jméno by mělo začínat abecedou nebo _",
"followingCharactersAreNotAllowed": "Následující znaky nejsou povoleny",
"columnNameRequired": "Název sloupce je povinný",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Název projektu přesahuje 50 znaků",
"projectNameCannotStartWithSpace": "Název projektu nesmí začínat mezerou",
"requiredField": "Povinné pole",

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

@ -74,7 +74,8 @@
"insertBefore": "Indsæt før",
"hideField": "Skjul felt",
"sortAsc": "Sortere stigende",
"sortDesc": "Sortere nedadgående"
"sortDesc": "Sortere nedadgående",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galleri",
"form": "Formular",
"kanban": "Kanban.",
"calendar": "Kalender"
"calendar": "Kalender",
"map": "Map"
},
"user": "Bruger",
"users": "Brugere",
@ -136,6 +138,7 @@
"Currency": "betalingsmiddel",
"Percent": "Procentdel",
"Duration": "Varighed",
"GeoData": "GeoData",
"Rating": "Bedømmelse",
"Formula": "Formel",
"Rollup": "Rul op",
@ -253,6 +256,9 @@
"barcodeFormat": "Stregkodeformat",
"qrCodeValueTooLong": "For mange tegn til en QR-kode",
"barcodeValueTooLong": "For mange tegn til en stregkode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Aggregate Function.",
"dbCreateIfNotExists": "DATABASE: Opret, hvis ikke eksisterer",
"clientKey": "Klientnøgle",
@ -452,6 +458,10 @@
"stackedBy": "Stablet af",
"chooseGroupingField": "Vælg et grupperingsfelt",
"addOrEditStack": "Tilføj / Rediger stak"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Skaberen kan oprette nye projekter og få adgang til alle inviterede projekter.",
"orgViewer": "Seeren har ikke lov til at oprette nye projekter, men kan få adgang til alle inviterede projekter."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rækker per side",
"upload": "Vælg fil for at uploade",
"upload_sub": "eller træk og slip filen",
@ -600,6 +615,7 @@
"gallery": "Tilføj Gallery View.",
"form": "Tilføj formularvisning",
"kanban": "Tilføj Kanban View.",
"map": "Add Map View",
"calendar": "Tilføj kalendervisning"
},
"tablesMetadataInSync": "Tabeller Metadata er synkroniseret",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Navnet skal starte med et alfabet eller _",
"followingCharactersAreNotAllowed": "Følgende tegn er ikke tilladt",
"columnNameRequired": "Kolonnens navn er påkrævet",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Projektnavnet overstiger 50 tegn",
"projectNameCannotStartWithSpace": "Projektnavnet kan ikke begynde med et mellemrum",
"requiredField": "Obligatorisk felt",

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

@ -74,7 +74,8 @@
"insertBefore": "davor einfügen",
"hideField": "Feld ausblenden",
"sortAsc": "Aufsteigend sortieren",
"sortDesc": "Absteigend sortieren"
"sortDesc": "Absteigend sortieren",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galerie",
"form": "Formular",
"kanban": "Kanban",
"calendar": "Kalender"
"calendar": "Kalender",
"map": "Karte"
},
"user": "Nutzer",
"users": "Benutzer",
@ -136,6 +138,7 @@
"Currency": "Währung",
"Percent": "Prozent",
"Duration": "Dauer",
"GeoData": "GeoData",
"Rating": "Klassifizierung",
"Formula": "Formel",
"Rollup": "Zusammenfassung",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode-Format",
"qrCodeValueTooLong": "Zu viele Zeichen für einen QR-Code",
"barcodeValueTooLong": "Zu viele Zeichen für einen Barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Globale Funktion",
"dbCreateIfNotExists": "Datenbank: Erstellen, falls nicht vorhanden",
"clientKey": "Client-Schlüssel",
@ -452,6 +458,10 @@
"stackedBy": "Gestapelt von",
"chooseGroupingField": "Wählen Sie ein Gruppierungsfeld",
"addOrEditStack": "Stapel hinzufügen / bearbeiten"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Der Ersteller kann neue Projekte erstellen und auf alle eingeladenen Projekte zugreifen.",
"orgViewer": "Betrachter können keine neuen Projekte erstellen, aber sie können auf alle eingeladenen Projekte zugreifen."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Zeilen pro Seite",
"upload": "Datei zum Hochladen auswählen",
"upload_sub": "oder Drag & Drop Datei",
@ -600,6 +615,7 @@
"gallery": "Galerie-Ansicht hinzufügen",
"form": "Formular-Ansicht hinzufügen",
"kanban": "Kanban-Ansicht hinzufügen",
"map": "Add Map View",
"calendar": "Kalender-Ansicht hinzufügen"
},
"tablesMetadataInSync": "Tabellen-Metadaten sind synchron",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name muss mit einem Buchstaben oder _ beginnen",
"followingCharactersAreNotAllowed": "Folgende Zeichen sind nicht erlaubt",
"columnNameRequired": "Spaltenname ist erforderlich",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Projektname überschreitet 50 Zeichen",
"projectNameCannotStartWithSpace": "Projektname darf nicht mit einem Leerzeichen beginnen",
"requiredField": "Pflichtfeld",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Project",
@ -98,7 +99,8 @@
"gallery": "Gallery",
"form": "Form",
"kanban": "Kanban",
"calendar": "Calendar"
"calendar": "Calendar",
"map": "Map"
},
"user": "User",
"users": "Users",
@ -136,6 +138,7 @@
"Currency": "Currency",
"Percent": "Percent",
"Duration": "Duration",
"GeoData": "GeoData",
"Rating": "Rating",
"Formula": "Formula",
"Rollup": "Rollup",
@ -254,6 +257,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Aggregate function",
"dbCreateIfNotExists": "Database : create if not exists",
"clientKey": "Client Key",
@ -455,6 +461,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -527,6 +537,11 @@
"moreThanOneRowFoundForCode": "More than one row found for this code. Currently only unique codes are supported.",
"noRowFoundForCode": "No row found for this code for the selected column"
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rows per page",
"upload": "Select file to Upload",
"upload_sub": "or drag and drop file",
@ -609,6 +624,7 @@
"gallery": "Add Gallery View",
"form": "Add Form View",
"kanban": "Add Kanban View",
"map": "Add Map View",
"calendar": "Add Calendar View"
},
"tablesMetadataInSync": "Tables metadata is in Sync",

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

@ -74,7 +74,8 @@
"insertBefore": "Insertar antes",
"hideField": "Ocultar campo",
"sortAsc": "Ordenación ascendente",
"sortDesc": "Orden descendente"
"sortDesc": "Orden descendente",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Proyecto",
@ -98,7 +99,8 @@
"gallery": "Galería",
"form": "Formulario",
"kanban": "Kanban",
"calendar": "Calendario"
"calendar": "Calendario",
"map": "Map"
},
"user": "Usuario",
"users": "Usuarios",
@ -136,6 +138,7 @@
"Currency": "Divisa",
"Percent": "Por ciento",
"Duration": "Duración",
"GeoData": "GeoData",
"Rating": "Clasificación",
"Formula": "Fórmula",
"Rollup": "Acumulado",
@ -253,6 +256,9 @@
"barcodeFormat": "Formato del código de barras",
"qrCodeValueTooLong": "Demasiados caracteres para un código QR",
"barcodeValueTooLong": "Demasiados caracteres para un código de barras",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Función agregada",
"dbCreateIfNotExists": "Base de datos : Crear si no existe",
"clientKey": "Clave de Cliente",
@ -452,6 +458,10 @@
"stackedBy": "Apilado por",
"chooseGroupingField": "Elija un campo de agrupación",
"addOrEditStack": "Añadir / Editar pila"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "El creador puede crear nuevos proyectos y acceder a cualquier proyecto invitado.",
"orgViewer": "El espectador no puede crear nuevos proyectos, pero puede acceder a cualquier proyecto invitado."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Filas por página",
"upload": "Selecciona Archivo para cargar",
"upload_sub": "o arrastra y suelta el archivo",
@ -600,6 +615,7 @@
"gallery": "Agregar vista de Galería",
"form": "Agregar vista de Formulario",
"kanban": "Agregar vista de Kanban",
"map": "Add Map View",
"calendar": "Agregar vista de Calendario"
},
"tablesMetadataInSync": "Sincronización de tablas de metadatos activa",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "El nombre debe empezar por un alfabeto o _",
"followingCharactersAreNotAllowed": "Los siguientes caracteres no están permitidos",
"columnNameRequired": "El nombre de la columna es obligatorio",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "El nombre del proyecto supera los 50 caracteres",
"projectNameCannotStartWithSpace": "El nombre del proyecto no puede empezar con un espacio",
"requiredField": "Campo obligatorio",

21
packages/nc-gui/lang/eu.json

@ -74,7 +74,8 @@
"insertBefore": "Txertatu aurretik",
"hideField": "Ezkutatu eremua",
"sortAsc": "Ordenatu gorantz",
"sortDesc": "Ordenatu beherantz"
"sortDesc": "Ordenatu beherantz",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Proiektua",
@ -98,7 +99,8 @@
"gallery": "Galeria",
"form": "Inprimakia",
"kanban": "Kanban",
"calendar": "Egutegia"
"calendar": "Egutegia",
"map": "Map"
},
"user": "Erabiltzailea",
"users": "Erabiltzaileak",
@ -136,6 +138,7 @@
"Currency": "Moneta",
"Percent": "Ehunekoa",
"Duration": "Iraupena",
"GeoData": "GeoData",
"Rating": "Balorazioa",
"Formula": "Ekuazioa",
"Rollup": "Rollup",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Aggregate function",
"dbCreateIfNotExists": "Database : create if not exists",
"clientKey": "Client Key",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rows per page",
"upload": "Select file to Upload",
"upload_sub": "or drag and drop file",
@ -600,6 +615,7 @@
"gallery": "Add Gallery View",
"form": "Add Form View",
"kanban": "Add Kanban View",
"map": "Add Map View",
"calendar": "Add Calendar View"
},
"tablesMetadataInSync": "Tables metadata is in Sync",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "پروژه",
@ -98,7 +99,8 @@
"gallery": "گالری",
"form": "فرم",
"kanban": "کانبان",
"calendar": "تقویم"
"calendar": "تقویم",
"map": "Map"
},
"user": "کاربر",
"users": "کاربران",
@ -136,6 +138,7 @@
"Currency": "واحد پول",
"Percent": "درصد",
"Duration": "مدت زمان",
"GeoData": "GeoData",
"Rating": "امتیاز",
"Formula": "فرمول",
"Rollup": "تجمیع کردن",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "تابع جمع",
"dbCreateIfNotExists": "پایگاه داده: ایجاد در صورت عدم وجود",
"clientKey": "کلید Client",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "تعداد ردیفها در هر صفحه",
"upload": "فایل را برای بارگذاری انتخاب کنید",
"upload_sub": "یا فایل را بکشید و رها کنید",
@ -600,6 +615,7 @@
"gallery": "افزودن نمایش گالری",
"form": "افزودن نمایش فرم",
"kanban": "افزودن نمایش کانبان",
"map": "Add Map View",
"calendar": "افزودن نمایش تقویم"
},
"tablesMetadataInSync": "متاداده جداول در حالت همگامسازی است",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Lisää ennen",
"hideField": "Piilota kenttä",
"sortAsc": "Lajittelu nouseva",
"sortDesc": "Lajittelu laskeva"
"sortDesc": "Lajittelu laskeva",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Hanke",
@ -98,7 +99,8 @@
"gallery": "Galleria",
"form": "Lomake",
"kanban": "Kanban",
"calendar": "Kalenteri"
"calendar": "Kalenteri",
"map": "Map"
},
"user": "Käyttäjä",
"users": "Käyttäjät",
@ -136,6 +138,7 @@
"Currency": "Valuutta",
"Percent": "Prosentti",
"Duration": "Kesto",
"GeoData": "GeoData",
"Rating": "Luokitus",
"Formula": "Kaava",
"Rollup": "Rullaus",
@ -253,6 +256,9 @@
"barcodeFormat": "Viivakoodin muoto",
"qrCodeValueTooLong": "Liian monta merkkiä QR-koodiin",
"barcodeValueTooLong": "Liikaa merkkejä viivakoodiin",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Kokonaistoiminto",
"dbCreateIfNotExists": "Tietokanta: Luo jos ei ole olemassa",
"clientKey": "Asiakasnäppäin",
@ -452,6 +458,10 @@
"stackedBy": "Pinottu by",
"chooseGroupingField": "Valitse ryhmittelykenttä",
"addOrEditStack": "Lisää / Muokkaa pinoa"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Luoja voi luoda uusia projekteja ja käyttää kaikkia kutsuttuja projekteja.",
"orgViewer": "Katsoja ei saa luoda uusia projekteja, mutta hän voi käyttää mitä tahansa kutsuttua projektia."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rivit sivua kohti",
"upload": "Valitse lataa tiedosto",
"upload_sub": "tai vedä ja pudota tiedosto",
@ -600,6 +615,7 @@
"gallery": "Lisää galleria-näkymä",
"form": "Lisää lomake",
"kanban": "Lisää Kanban View",
"map": "Add Map View",
"calendar": "Lisää kalenteri-näkymä"
},
"tablesMetadataInSync": "Taulukot Metatieto on synkronoitu",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Nimen on alettava aakkosilla tai _",
"followingCharactersAreNotAllowed": "Seuraavat merkit eivät ole sallittuja",
"columnNameRequired": "Sarakkeen nimi vaaditaan",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Hankkeen nimi ylittää 50 merkkiä",
"projectNameCannotStartWithSpace": "Projektin nimi ei voi alkaa välilyönnillä",
"requiredField": "Pakollinen kenttä",

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

@ -74,7 +74,8 @@
"insertBefore": "Insérer avant",
"hideField": "Masquer le champ",
"sortAsc": "Trier par ordre croissant",
"sortDesc": "Trier par ordre décroissant"
"sortDesc": "Trier par ordre décroissant",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projet",
@ -98,7 +99,8 @@
"gallery": "Galerie",
"form": "Formulaire",
"kanban": "Kanban",
"calendar": "Calendrier"
"calendar": "Calendrier",
"map": "Map"
},
"user": "Utilisateur",
"users": "Utilisateurs",
@ -136,6 +138,7 @@
"Currency": "Devise",
"Percent": "Pourcentage",
"Duration": "Durée",
"GeoData": "GeoData",
"Rating": "Évaluation",
"Formula": "Formule",
"Rollup": "Synthèse",
@ -253,6 +256,9 @@
"barcodeFormat": "Format du code-barres",
"qrCodeValueTooLong": "Trop de caractères pour un code QR",
"barcodeValueTooLong": "Trop de caractères pour un code-barres",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Fonction agrégée",
"dbCreateIfNotExists": "Base de données : la créer si elle n'existe pas",
"clientKey": "Clé client",
@ -452,6 +458,10 @@
"stackedBy": "Empilés par",
"chooseGroupingField": "Choisir un champ de regroupement",
"addOrEditStack": "Ajouter / Modifier une pile"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Le créateur peut créer de nouveaux projets et accéder à tout projet invité.",
"orgViewer": "Le visualisateur n'est pas autorisé à créer de nouveaux projets mais il peut accéder à tout projet invité."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Lignes par page",
"upload": "Sélectionner un fichier à téléverser",
"upload_sub": "ou glisser-déposer un fichier",
@ -600,6 +615,7 @@
"gallery": "Ajouter une vue Galerie",
"form": "Ajouter une vue Formulaire",
"kanban": "Ajouter une vue Kanban",
"map": "Add Map View",
"calendar": "Ajouter une vue Calendrier"
},
"tablesMetadataInSync": "Les métadonnées de tables sont en synchronisation",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Le nom doit commencer par une lettre de l'alphabet ou _",
"followingCharactersAreNotAllowed": "Les caractères suivants ne sont pas autorisés",
"columnNameRequired": "Nom de la colonne requis",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Le nom du projet dépasse les 50 caractères",
"projectNameCannotStartWithSpace": "Le nom du projet ne peut pas commencer par un espace",
"requiredField": "Champ requis",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": ּרוֹיֶקט",
@ -98,7 +99,8 @@
"gallery": "גלריה",
"form": "טופס",
"kanban": "קאנב\"כ",
"calendar": "לוּחַ שָׁנָה"
"calendar": "לוּחַ שָׁנָה",
"map": "Map"
},
"user": ִשׁתַמֵשׁ",
"users": "משתמשים.",
@ -136,6 +138,7 @@
"Currency": ַטְבֵּעַ",
"Percent": ָחוּז",
"Duration": "אורך זמן",
"GeoData": "GeoData",
"Rating": "דירוג",
"Formula": "נוּסחָה",
"Rollup": "Rullup",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "פונקציה מצטברת",
"dbCreateIfNotExists": "מסד נתונים: צור אם לא קיים",
"clientKey": "מפתח הלקוח",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "שורות לדף",
"upload": "בחר קובץ להעלאה",
"upload_sub": "או גרור ושחרר קובץ",
@ -600,6 +615,7 @@
"gallery": "הוסף תצוגת גלריה",
"form": "הוסף טופס View.",
"kanban": "הוסף Kanban View.",
"map": "Add Map View",
"calendar": "הוסף תצוגת לוח שנה"
},
"tablesMetadataInSync": "טבלאות מטא נתונים מסונכרנים",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "परिजन",
@ -98,7 +99,8 @@
"gallery": "गलर",
"form": "परपतर",
"kanban": "Kanban",
"calendar": "पग"
"calendar": "पग",
"map": "Map"
},
"user": "उपयगकर",
"users": "उपयगकर",
@ -136,6 +138,7 @@
"Currency": "म",
"Percent": "परतिशत",
"Duration": "अवधि",
"GeoData": "GeoData",
"Rating": "रिग",
"Formula": "सर",
"Rollup": "जमन",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "कल समह",
"dbCreateIfNotExists": "डस: बन यदिद नह",
"clientKey": "गहक क",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "परतिठ पि",
"upload": "अपलड करनिए फइल क चयन कर",
"upload_sub": "यग एड डप फइल",
@ -600,6 +615,7 @@
"gallery": "गलरय ज",
"form": "फम व",
"kanban": "कनबन दय ज",
"map": "Add Map View",
"calendar": "कडर दय ज"
},
"tablesMetadataInSync": "टबल क SYNC ह",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galerija",
"form": "Oblik",
"kanban": "Kanban",
"calendar": "Kalendar"
"calendar": "Kalendar",
"map": "Map"
},
"user": "Korisnik",
"users": "Korisnik",
@ -136,6 +138,7 @@
"Currency": "Valuta",
"Percent": "Postotak",
"Duration": "Trajanje",
"GeoData": "GeoData",
"Rating": "Ocjena",
"Formula": "Formula",
"Rollup": "Valjak",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Agregatna funkcija",
"dbCreateIfNotExists": "Baza podataka: stvoriti ako ne postoji",
"clientKey": "Ključ klijenta",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Redaka po stranici",
"upload": "Odaberite datoteku za prijenos",
"upload_sub": "ili povucite i ispustite datoteku",
@ -600,6 +615,7 @@
"gallery": "Dodajte pogled na galeriju",
"form": "Dodajte prikaz obrasca",
"kanban": "Dodajte Kanban pogled",
"map": "Add Map View",
"calendar": "Dodajte prikaz kalendara"
},
"tablesMetadataInSync": "Metapodaci tablice se sinkroniziraju",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Sisipkan Sebelum",
"hideField": "Sembunyikan Bidang",
"sortAsc": "Urutkan Menaik",
"sortDesc": "Urutkan Menurun"
"sortDesc": "Urutkan Menurun",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Proyek",
@ -98,7 +99,8 @@
"gallery": "Galeri",
"form": "Membentuk",
"kanban": "Kanban.",
"calendar": "Kalender"
"calendar": "Kalender",
"map": "Map"
},
"user": "Pengguna",
"users": "Pengguna.",
@ -136,6 +138,7 @@
"Currency": "Mata uang",
"Percent": "Persen",
"Duration": "Durasi",
"GeoData": "GeoData",
"Rating": "Peringkat",
"Formula": "Rumus",
"Rollup": "Rollup.",
@ -253,6 +256,9 @@
"barcodeFormat": "Format kode batang",
"qrCodeValueTooLong": "Terlalu banyak karakter untuk kode QR",
"barcodeValueTooLong": "Terlalu banyak karakter untuk barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Fungsi agregat.",
"dbCreateIfNotExists": "Basis Data: Buat jika tidak ada",
"clientKey": "Kunci klien",
@ -452,6 +458,10 @@
"stackedBy": "Ditumpuk oleh",
"chooseGroupingField": "Pilih Bidang Pengelompokan",
"addOrEditStack": "Tambah / Edit Tumpukan"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Kreator dapat membuat proyek baru dan mengakses proyek yang diundang.",
"orgViewer": "Penonton tidak diizinkan untuk membuat proyek baru, tetapi mereka dapat mengakses proyek yang diundang."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Baris per halaman",
"upload": "Pilih file untuk diunggah",
"upload_sub": "atau seret dan jatuhkan file",
@ -600,6 +615,7 @@
"gallery": "Tambahkan Tampilan Galeri",
"form": "Tambahkan tampilan formulir",
"kanban": "Tambahkan Kanban Lihat",
"map": "Add Map View",
"calendar": "Tambahkan tampilan kalender"
},
"tablesMetadataInSync": "Tabel metadata sedang disinkronkan",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Nama harus dimulai dengan alfabet atau _",
"followingCharactersAreNotAllowed": "Karakter berikut tidak diperbolehkan",
"columnNameRequired": "Nama kolom harus diisi",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Nama proyek melebihi 50 karakter",
"projectNameCannotStartWithSpace": "Nama proyek tidak boleh dimulai dengan spasi",
"requiredField": "Bidang yang dibutuhkan",

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

@ -74,7 +74,8 @@
"insertBefore": "Inserire prima",
"hideField": "Campo nascosto",
"sortAsc": "Ordinamento crescente",
"sortDesc": "Ordinamento decrescente"
"sortDesc": "Ordinamento decrescente",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Progetto",
@ -98,7 +99,8 @@
"gallery": "Galleria",
"form": "Modulo",
"kanban": "Kanban",
"calendar": "Calendario"
"calendar": "Calendario",
"map": "Map"
},
"user": "Utente",
"users": "Utenti",
@ -136,6 +138,7 @@
"Currency": "Moneta",
"Percent": "Percento",
"Duration": "Durata",
"GeoData": "GeoData",
"Rating": "Valutazione",
"Formula": "Formula",
"Rollup": "Rollup",
@ -253,6 +256,9 @@
"barcodeFormat": "Formato del codice a barre",
"qrCodeValueTooLong": "Troppi caratteri per un codice QR",
"barcodeValueTooLong": "Troppi caratteri per un codice a barre",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Aggrega funzione",
"dbCreateIfNotExists": "Database: crea se non esiste",
"clientKey": "Chiave client",
@ -452,6 +458,10 @@
"stackedBy": "Impilato da",
"chooseGroupingField": "Scegliere un campo di raggruppamento",
"addOrEditStack": "Aggiungere / modificare la pila"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Il Creatore può creare nuovi progetti e accedere a qualsiasi progetto invitato.",
"orgViewer": "Il visualizzatore non può creare nuovi progetti, ma può accedere a qualsiasi progetto invitato."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Righe per pagina",
"upload": "Seleziona il file da caricare",
"upload_sub": "o trascinalo qui",
@ -600,6 +615,7 @@
"gallery": "Aggiungi vista galleria",
"form": "Aggiungi vista modulo",
"kanban": "Aggiungi vista kanban",
"map": "Add Map View",
"calendar": "Aggiungi vista calendario"
},
"tablesMetadataInSync": "I metadati delle tabelle sono sincronizzati",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Il nome deve iniziare con un alfabeto o con _",
"followingCharactersAreNotAllowed": "I seguenti caratteri non sono ammessi",
"columnNameRequired": "Il nome della colonna è richiesto",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Il nome del progetto supera i 50 caratteri",
"projectNameCannotStartWithSpace": "Il nome del progetto non può iniziare con uno spazio",
"requiredField": "Campo obbligatorio",

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

@ -74,7 +74,8 @@
"insertBefore": "前に挿入",
"hideField": "フィールドを隠す",
"sortAsc": "昇順",
"sortDesc": "降順"
"sortDesc": "降順",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "プロジェクト",
@ -98,7 +99,8 @@
"gallery": "ギャラリー",
"form": "フォーム",
"kanban": "カンバン",
"calendar": "カレンダー"
"calendar": "カレンダー",
"map": "Map"
},
"user": "ユーザー",
"users": "ユーザー",
@ -136,6 +138,7 @@
"Currency": "通貨",
"Percent": "パーセント",
"Duration": "間隔",
"GeoData": "GeoData",
"Rating": "レーティング",
"Formula": "方式",
"Rollup": "ロールアップ",
@ -253,6 +256,9 @@
"barcodeFormat": "バーコードのフォーマット",
"qrCodeValueTooLong": "QRコードにするには文字数が多すぎる",
"barcodeValueTooLong": "バーコードの文字数が多すぎる",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "集約関数",
"dbCreateIfNotExists": "データベース:存在しない場合は作成",
"clientKey": "クライアントキー",
@ -452,6 +458,10 @@
"stackedBy": "スタック",
"chooseGroupingField": "グループ化するフィールドを選択",
"addOrEditStack": "スタックの追加/編集"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "「作成者」は新しいプロジェクトを作成でき、かつ招待されたプロジェクトにもアクセスできます。",
"orgViewer": "ビューアーは新規プロジェクトを作成することはできませんが、招待されたプロジェクトにアクセスできます。"
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "1ページあたりの行数",
"upload": "アップロードするファイルを選択してください",
"upload_sub": "またはファイルをドラッグ & ドロップ",
@ -600,6 +615,7 @@
"gallery": "ギャラリービューを追加",
"form": "フォームビューを追加",
"kanban": "カンバンビューを追加",
"map": "Add Map View",
"calendar": "カレンダービューを追加"
},
"tablesMetadataInSync": "テーブルメタデータは同期されています",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "名前はアルファベットまたは_で始まる必要があります",
"followingCharactersAreNotAllowed": "以下の文字種は使用できません",
"columnNameRequired": "列名が必要です",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "プロジェクト名が50文字を超えています",
"projectNameCannotStartWithSpace": "プロジェクト名の先頭にはスペースは利用できません",
"requiredField": "必須フィールド",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "프로젝트",
@ -98,7 +99,8 @@
"gallery": "갤러리",
"form": "폼",
"kanban": "칸반",
"calendar": "캘린더"
"calendar": "캘린더",
"map": "Map"
},
"user": "사용자",
"users": "사용자",
@ -136,6 +138,7 @@
"Currency": "통화",
"Percent": "퍼센트",
"Duration": "기간",
"GeoData": "GeoData",
"Rating": "등급",
"Formula": "공식",
"Rollup": "롤업",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "집합 함수",
"dbCreateIfNotExists": "데이터베이스 : 존재하지 않는 경우 생성",
"clientKey": "클라이언트 키",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "페이지 당 행",
"upload": "업로드 할 파일 선택",
"upload_sub": "또는 끌어서 놓기 파일",
@ -600,6 +615,7 @@
"gallery": "갤러리 뷰 추가",
"form": "폼 뷰 추가",
"kanban": "칸반 뷰 추가",
"map": "Add Map View",
"calendar": "캘린더 뷰 추가"
},
"tablesMetadataInSync": "테이블 메타 데이터가 동기화되어 있습니다",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Ievietot pirms",
"hideField": "Slēpt lauku",
"sortAsc": "Kārtot augošā secībā",
"sortDesc": "Kārtot dilstošā secībā"
"sortDesc": "Kārtot dilstošā secībā",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekts",
@ -98,7 +99,8 @@
"gallery": "Galerija",
"form": "Forma",
"kanban": "Kanban",
"calendar": "Kalendārs"
"calendar": "Kalendārs",
"map": "Map"
},
"user": "Lietotājs",
"users": "Lietotāji",
@ -136,6 +138,7 @@
"Currency": "Valūta",
"Percent": "Procenti",
"Duration": "Ilgums",
"GeoData": "GeoData",
"Rating": "Vērtējums",
"Formula": "Formula",
"Rollup": "Apkopojums",
@ -253,6 +256,9 @@
"barcodeFormat": "Svītrkoda formāts",
"qrCodeValueTooLong": "Pārāk daudz rakstzīmju QR kodam",
"barcodeValueTooLong": "Pārāk daudz zīmju svītrkodam",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Agregācijas funkcija",
"dbCreateIfNotExists": "Datubāze : izveidotm ja neeksistē",
"clientKey": "Klienta atslēga",
@ -452,6 +458,10 @@
"stackedBy": "Sakrautas ar",
"chooseGroupingField": "Izvēlieties grupēšanas lauku",
"addOrEditStack": "Pievienot / rediģēt kaudzi"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Radītājs var izveidot jaunus projektus un piekļūt jebkuram uzaicinātajam projektam.",
"orgViewer": "Skatītājs nedrīkst veidot jaunus projektus, bet var piekļūt jebkuram uzaicinātajam projektam."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "ieraksti lapā",
"upload": "Izvēlēties datni augšupielādei",
"upload_sub": "vai vilkt un nomest datni",
@ -600,6 +615,7 @@
"gallery": "Pievienot galerijas skatu",
"form": "Pievienot formas skatu",
"kanban": "Pievienot Kanban skatu",
"map": "Add Map View",
"calendar": "Pievienot kalendāra skatu"
},
"tablesMetadataInSync": "Tabulu metadati ir sinhronizācijā",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Vārdam jāsākas ar alfabētu vai _",
"followingCharactersAreNotAllowed": "Šādas rakstzīmes nav atļautas",
"columnNameRequired": "Slejas nosaukums ir obligāts",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Projekta nosaukums pārsniedz 50 rakstzīmes",
"projectNameCannotStartWithSpace": "Projekta nosaukums nedrīkst sākties ar atstarpi",
"requiredField": "Obligātais lauks",

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

@ -74,7 +74,8 @@
"insertBefore": "Invoegen voor",
"hideField": "Verberg veld",
"sortAsc": "Oplopend sorteren",
"sortDesc": "Aflopend sorteren"
"sortDesc": "Aflopend sorteren",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Project",
@ -98,7 +99,8 @@
"gallery": "Galerij",
"form": "Formulier",
"kanban": "Kanban",
"calendar": "Kalender"
"calendar": "Kalender",
"map": "Map"
},
"user": "Gebruiker",
"users": "Gebruikers",
@ -136,6 +138,7 @@
"Currency": "Munteenheid",
"Percent": "Procent",
"Duration": "Looptijd",
"GeoData": "GeoData",
"Rating": "Beoordeling",
"Formula": "Formule",
"Rollup": "Rollup",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode formaat",
"qrCodeValueTooLong": "Te veel tekens voor een QR-code",
"barcodeValueTooLong": "Te veel tekens voor een streepjescode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Geaggregeerde functie",
"dbCreateIfNotExists": "Database: creëer als het niet bestaat",
"clientKey": "Klantensleutel",
@ -452,6 +458,10 @@
"stackedBy": "Gestapeld door",
"chooseGroupingField": "Kies een groepeerveld",
"addOrEditStack": "Stapel toevoegen / bewerken"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Creator kan nieuwe projecten aanmaken en heeft toegang tot elk uitgenodigd project.",
"orgViewer": "De Viewer mag geen nieuwe projecten aanmaken, maar heeft wel toegang tot elk uitgenodigd project."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rijen per pagina",
"upload": "Selecteer bestand om te uploaden",
"upload_sub": "of sleep het bestand naar hier",
@ -600,6 +615,7 @@
"gallery": "Gallerijweergave toevoegen",
"form": "Formulierweergave toevoegen",
"kanban": "Kanbanweergave toevoegen",
"map": "Add Map View",
"calendar": "Kalenderweergave toevoegen"
},
"tablesMetadataInSync": "Tabelmetadata is gesynchroniseerd",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "De naam moet beginnen met een alfabet of _",
"followingCharactersAreNotAllowed": "De volgende tekens zijn niet toegestaan",
"columnNameRequired": "Kolomnaam is verplicht",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Projectnaam meer dan 50 tekens",
"projectNameCannotStartWithSpace": "Projectnaam kan niet beginnen met een spatie",
"requiredField": "Verplicht veld",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Prosjekt",
@ -98,7 +99,8 @@
"gallery": "Galleri",
"form": "Skjema",
"kanban": "Kanban",
"calendar": "Kalender"
"calendar": "Kalender",
"map": "Map"
},
"user": "Bruker",
"users": "Brukere",
@ -136,6 +138,7 @@
"Currency": "Valuta",
"Percent": "Prosent",
"Duration": "Varighet",
"GeoData": "GeoData",
"Rating": "Vurdering",
"Formula": "Formel",
"Rollup": "Rull opp",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Samlet funksjon",
"dbCreateIfNotExists": "Database: Opprett hvis ikke eksisterer",
"clientKey": "Klientnøkkel",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rader per side.",
"upload": "Velg Fil for å laste opp",
"upload_sub": "eller dra og slipp filen",
@ -600,6 +615,7 @@
"gallery": "Legg til galleriutsikt",
"form": "Legg til skjemavisning",
"kanban": "Legg til Kanban View",
"map": "Add Map View",
"calendar": "Legg til kalendervisning"
},
"tablesMetadataInSync": "Tabeller Metadata er synkronisert",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Wstaw przed",
"hideField": "Ukryj pole",
"sortAsc": "Sortowanie rosnące",
"sortDesc": "Sortuj malejąco"
"sortDesc": "Sortuj malejąco",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galeria",
"form": "Formularz",
"kanban": "Kanban",
"calendar": "Kalendarz"
"calendar": "Kalendarz",
"map": "Map"
},
"user": "Użytkownik",
"users": "Użytkownicy",
@ -136,6 +138,7 @@
"Currency": "Waluta",
"Percent": "Procent",
"Duration": "Czas trwania",
"GeoData": "GeoData",
"Rating": "Ocena",
"Formula": "Formuła",
"Rollup": "Zliczanie",
@ -253,6 +256,9 @@
"barcodeFormat": "Format kodu kreskowego",
"qrCodeValueTooLong": "Zbyt wiele znaków dla kodu QR",
"barcodeValueTooLong": "Zbyt wiele znaków dla kodu kreskowego",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Funkcja agregacji",
"dbCreateIfNotExists": "Baza danych: Utwórz, jeśli nie istnieje",
"clientKey": "Klucz klienta",
@ -452,6 +458,10 @@
"stackedBy": "Ułożone według",
"chooseGroupingField": "Wybierz pole grupowania",
"addOrEditStack": "Dodaj / Edytuj stos"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Twórca może tworzyć nowe projekty i mieć dostęp do każdego zaproszonego projektu.",
"orgViewer": "Widz nie może tworzyć nowych projektów, ale może mieć dostęp do każdego zaproszonego projektu."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Wiersze na stronę",
"upload": "Wybierz plik do przesłania",
"upload_sub": "lub przeciągnij i upuść plik",
@ -600,6 +615,7 @@
"gallery": "Dodaj widok galerii.",
"form": "Dodaj widok formy",
"kanban": "Dodaj widok Kanban.",
"map": "Add Map View",
"calendar": "Dodaj widok kalendarza"
},
"tablesMetadataInSync": "Tabele Metadane są synchronizowane",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Nazwa powinna zaczynać się od alfabetu lub od _",
"followingCharactersAreNotAllowed": "Następujące znaki są niedozwolone",
"columnNameRequired": "Nazwa kolumny jest wymagana",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Nazwa projektu przekracza 50 znaków",
"projectNameCannotStartWithSpace": "Nazwa projektu nie może zaczynać się od spacji",
"requiredField": "Pole wymagane",

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

@ -74,7 +74,8 @@
"insertBefore": "Inserir Antes",
"hideField": "Ocultar Campo",
"sortAsc": "Ordenar Ascendente",
"sortDesc": "Ordenar Descendente"
"sortDesc": "Ordenar Descendente",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projeto",
@ -98,7 +99,8 @@
"gallery": "Galeria",
"form": "Formulário",
"kanban": "Kanban",
"calendar": "Calendário"
"calendar": "Calendário",
"map": "Map"
},
"user": "Do utilizador",
"users": "Comercial",
@ -136,6 +138,7 @@
"Currency": "Moeda",
"Percent": "Por cento",
"Duration": "Duração",
"GeoData": "GeoData",
"Rating": "Avaliação",
"Formula": "Fórmula",
"Rollup": "Rolar",
@ -253,6 +256,9 @@
"barcodeFormat": "Formato do código de barras",
"qrCodeValueTooLong": "Demasiados caracteres para um código QR",
"barcodeValueTooLong": "Demasiados caracteres para um código de barras",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Função agregada",
"dbCreateIfNotExists": "Base de Dados : criar se não existir",
"clientKey": "Chave do Cliente",
@ -452,6 +458,10 @@
"stackedBy": "Empilhado por",
"chooseGroupingField": "Escolha um Campo de Agrupamento",
"addOrEditStack": "Adicionar / Editar Pilha"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "O criador pode criar novos projectos e aceder a qualquer projecto convidado.",
"orgViewer": "O espectador não está autorizado a criar novos projectos, mas pode aceder a qualquer projecto convidado."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Linhas por página",
"upload": "Selecione Arquivo para upload",
"upload_sub": "ou arrastar e soltar arquivo",
@ -600,6 +615,7 @@
"gallery": "Adicionar vista de galeria",
"form": "Adicionar vista de formulário",
"kanban": "Adicionar vista de Kanban",
"map": "Add Map View",
"calendar": "Adicionar vista de calendário"
},
"tablesMetadataInSync": "Metadados de tabelas está em sincronia",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "O nome deve começar com um alfabeto ou _",
"followingCharactersAreNotAllowed": "Os seguintes caracteres não são permitidos",
"columnNameRequired": "O nome da coluna é obrigatório",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "O nome do projecto excede 50 caracteres",
"projectNameCannotStartWithSpace": "O nome do projecto não pode começar com espaço",
"requiredField": "Campo obrigatório",

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

@ -74,7 +74,8 @@
"insertBefore": "Inserir Antes",
"hideField": "Ocultar Campo",
"sortAsc": "Ordenar Ascendente",
"sortDesc": "Ordenar Descendente"
"sortDesc": "Ordenar Descendente",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projeto",
@ -98,7 +99,8 @@
"gallery": "Galeria",
"form": "Formulário",
"kanban": "Kanban",
"calendar": "Calendário"
"calendar": "Calendário",
"map": "Map"
},
"user": "Do utilizador",
"users": "Comercial",
@ -136,6 +138,7 @@
"Currency": "Moeda",
"Percent": "Por cento",
"Duration": "Duração",
"GeoData": "GeoData",
"Rating": "Avaliação",
"Formula": "Fórmula",
"Rollup": "Rolar",
@ -253,6 +256,9 @@
"barcodeFormat": "Formato do código de barras",
"qrCodeValueTooLong": "Demasiados caracteres para um código QR",
"barcodeValueTooLong": "Demasiados caracteres para um código de barras",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Função agregada",
"dbCreateIfNotExists": "Base de Dados : criar se não existir",
"clientKey": "Chave do Cliente",
@ -452,6 +458,10 @@
"stackedBy": "Empilhado por",
"chooseGroupingField": "Escolha um Campo de Agrupamento",
"addOrEditStack": "Adicionar / Editar Pilha"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "O criador pode criar novos projectos e aceder a qualquer projecto convidado.",
"orgViewer": "O espectador não está autorizado a criar novos projectos, mas pode aceder a qualquer projecto convidado."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Linhas por página",
"upload": "Selecione Arquivo para upload",
"upload_sub": "ou arrastar e soltar arquivo",
@ -600,6 +615,7 @@
"gallery": "Adicionar vizualização de galeria",
"form": "Adicionar vizualização de formulário",
"kanban": "Adicionar vizualização de Kanban",
"map": "Add Map View",
"calendar": "Adicionar vizualização de calendário"
},
"tablesMetadataInSync": "Metadados de tabelas está em sincronia",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "O nome deve começar com um alfabeto ou _",
"followingCharactersAreNotAllowed": "Os seguintes caracteres não são permitidos",
"columnNameRequired": "O nome da coluna é obrigatório",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "O nome do projecto excede 50 caracteres",
"projectNameCannotStartWithSpace": "O nome do projecto não pode começar com espaço",
"requiredField": "Campo obrigatório",

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

@ -74,7 +74,8 @@
"insertBefore": "Вставить перед",
"hideField": "Скрыть поле",
"sortAsc": "По Возрастанию",
"sortDesc": "По убыванию"
"sortDesc": "По убыванию",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Проект",
@ -98,7 +99,8 @@
"gallery": "Галерея",
"form": "Форма",
"kanban": "Канбан",
"calendar": "Календарь"
"calendar": "Календарь",
"map": "Map"
},
"user": "Пользователь",
"users": "Пользователи",
@ -136,6 +138,7 @@
"Currency": "Валюта",
"Percent": "Процент",
"Duration": "Интервал времени",
"GeoData": "GeoData",
"Rating": "Рейтинг",
"Formula": "Формула",
"Rollup": "Итоги (Rollup)",
@ -253,6 +256,9 @@
"barcodeFormat": "Формат штрих-кода",
"qrCodeValueTooLong": "Слишком много символов для QR-кода",
"barcodeValueTooLong": "Слишком много символов для штрихкода",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Агрегатная функция",
"dbCreateIfNotExists": "База данных: создать, если не существует",
"clientKey": "Ключ клиента",
@ -452,6 +458,10 @@
"stackedBy": "Группировка по",
"chooseGroupingField": "Выберите поле группировки",
"addOrEditStack": "Добавление / редактирование стека"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Создатель может создавать новые проекты и получать доступ к любому приглашенному проекту.",
"orgViewer": "Наблюдатель не может создавать новые проекты, но может получить доступ к любому проекту по приглашению."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Количество строк на странице",
"upload": "Выберите файл для загрузки",
"upload_sub": "или перетащите файл",
@ -600,6 +615,7 @@
"gallery": "Добавить представление галерея",
"form": "Добавить представление форма",
"kanban": "Добавить представление Канбан",
"map": "Add Map View",
"calendar": "Добавить представление календарь"
},
"tablesMetadataInSync": "Таблицы метаданные синхронизируются",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Имя должно начинаться с алфавита или _",
"followingCharactersAreNotAllowed": "Нельзя использовать следующие символы",
"columnNameRequired": "Требуется название столбца",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Название проекта превышает 50 символов",
"projectNameCannotStartWithSpace": "Название проекта не может начинаться с пробела",
"requiredField": "Обязательное поле",

21
packages/nc-gui/lang/sk.json

@ -74,7 +74,8 @@
"insertBefore": "Vložiť pred",
"hideField": "Skryť pole",
"sortAsc": "Zoradiť vzostupne",
"sortDesc": "Zoradiť zostupne"
"sortDesc": "Zoradiť zostupne",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galéria",
"form": "Formulár",
"kanban": "Kanban",
"calendar": "Kalendár"
"calendar": "Kalendár",
"map": "Map"
},
"user": "Používateľ",
"users": "Používatelia",
@ -136,6 +138,7 @@
"Currency": "Mena",
"Percent": "Percentá",
"Duration": "Trvanie",
"GeoData": "GeoData",
"Rating": "Hodnotenie",
"Formula": "Vzorec",
"Rollup": "Rollup",
@ -253,6 +256,9 @@
"barcodeFormat": "Formát čiarového kódu",
"qrCodeValueTooLong": "Príliš veľa znakov pre kód QR",
"barcodeValueTooLong": "Príliš veľa znakov pre čiarový kód",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Súhrnná funkcia",
"dbCreateIfNotExists": "Databáza : vytvoriť, ak neexistuje",
"clientKey": "Kľúč klienta",
@ -452,6 +458,10 @@
"stackedBy": "Naskladané podľa",
"chooseGroupingField": "Výber zoskupovacieho poľa",
"addOrEditStack": "Pridať / upraviť zásobník"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Creator môže vytvárať nové projekty a pristupovať k ľubovoľnému pozvanému projektu.",
"orgViewer": "Prehliadač nemôže vytvárať nové projekty, ale môže pristupovať k ľubovoľnému pozvanému projektu."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Riadky na stránku",
"upload": "Vyberte súbor na odoslanie",
"upload_sub": "alebo potiahnite a pustite súbor",
@ -600,6 +615,7 @@
"gallery": "Pridanie zobrazenia galérie",
"form": "Pridanie zobrazenia formulára",
"kanban": "Pridanie zobrazenia Kanban",
"map": "Add Map View",
"calendar": "Pridanie zobrazenia kalendára"
},
"tablesMetadataInSync": "Metadáta tabuliek sú synchronizované",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Meno by malo začínať abecedou alebo _",
"followingCharactersAreNotAllowed": "Nasledujúce znaky nie sú povolené",
"columnNameRequired": "Vyžaduje sa názov stĺpca",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Názov projektu presahuje 50 znakov",
"projectNameCannotStartWithSpace": "Názov projektu nemôže začínať medzerou",
"requiredField": "Povinné pole",

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

@ -74,7 +74,8 @@
"insertBefore": "Vstavljanje pred",
"hideField": "Skrij polje",
"sortAsc": "Razvrsti naraščajoče",
"sortDesc": "Razvrsti padajoče"
"sortDesc": "Razvrsti padajoče",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Project.",
@ -98,7 +99,8 @@
"gallery": "Galerija",
"form": "Obrazec",
"kanban": "Kanban",
"calendar": "Koledar"
"calendar": "Koledar",
"map": "Map"
},
"user": "Uporabnik",
"users": "Uporabniki",
@ -136,6 +138,7 @@
"Currency": "Valuta",
"Percent": "Odstotek",
"Duration": "Trajanje",
"GeoData": "GeoData",
"Rating": "Ocena",
"Formula": "Formula",
"Rollup": "Zavihamo",
@ -253,6 +256,9 @@
"barcodeFormat": "Format črtne kode",
"qrCodeValueTooLong": "Preveč znakov za kodo QR",
"barcodeValueTooLong": "Preveč znakov za črtno kodo",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Agregatna funkcija",
"dbCreateIfNotExists": "Baza podatkov: Ustvari, če ne obstaja",
"clientKey": "Odjemalski ključ",
@ -452,6 +458,10 @@
"stackedBy": "Zloženo po",
"chooseGroupingField": "Izberite polje za združevanje",
"addOrEditStack": "Dodajanje / urejanje sklada"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Ustvarjalec lahko ustvarja nove projekte in dostopa do vseh povabljenih projektov.",
"orgViewer": "Pregledovalec ne sme ustvarjati novih projektov, lahko pa dostopa do vseh povabljenih projektov."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Vrstice na stran.",
"upload": "Izberite datoteko za nalaganje",
"upload_sub": "ali datoteko povlecite in spustite",
@ -600,6 +615,7 @@
"gallery": "Dodaj ogled galerije",
"form": "Dodaj pogled obrazca",
"kanban": "Dodaj pogled Kanban-a",
"map": "Add Map View",
"calendar": "Dodaj pogled koledarja"
},
"tablesMetadataInSync": "Tabele metapodatkov je v sinhronizaciji",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Ime se mora začeti z abecedo ali _",
"followingCharactersAreNotAllowed": "Naslednji znaki niso dovoljeni",
"columnNameRequired": "Ime stolpca je obvezno",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Ime projekta presega 50 znakov",
"projectNameCannotStartWithSpace": "Ime projekta se ne sme začeti s presledkom",
"requiredField": "Obvezno polje",

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

@ -74,7 +74,8 @@
"insertBefore": "Infoga före",
"hideField": "Dölj fältet",
"sortAsc": "Sortera i stigande riktning",
"sortDesc": "Sortera fallande"
"sortDesc": "Sortera fallande",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Projekt",
@ -98,7 +99,8 @@
"gallery": "Galleri",
"form": "Formulär",
"kanban": "Kanban",
"calendar": "Kalender"
"calendar": "Kalender",
"map": "Map"
},
"user": "Användare",
"users": "Användare",
@ -136,6 +138,7 @@
"Currency": "Valuta",
"Percent": "Procent",
"Duration": "Varaktighet",
"GeoData": "GeoData",
"Rating": "Betyg",
"Formula": "Formel",
"Rollup": "Rulla upp",
@ -253,6 +256,9 @@
"barcodeFormat": "Streckkodsformat",
"qrCodeValueTooLong": "För många tecken för en QR-kod",
"barcodeValueTooLong": "För många tecken för en streckkod",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Aggregatfunktion",
"dbCreateIfNotExists": "Databas: Skapa om det inte finns",
"clientKey": "Klientnyckel",
@ -452,6 +458,10 @@
"stackedBy": "Staplade av",
"chooseGroupingField": "Välj ett grupperingsfält",
"addOrEditStack": "Lägg till/redigera stacken"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Skaparen kan skapa nya projekt och få tillgång till alla inbjudna projekt.",
"orgViewer": "Visaren får inte skapa nya projekt, men kan få tillgång till alla inbjudna projekt."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Rader per sida",
"upload": "Välj fil för att ladda upp",
"upload_sub": "eller dra och släpp filen",
@ -600,6 +615,7 @@
"gallery": "Lägg till Gallery View",
"form": "Lägg till formulärvy",
"kanban": "Lägg till Kanban View",
"map": "Add Map View",
"calendar": "Lägg till kalendervisning"
},
"tablesMetadataInSync": "Tabeller Metadata är synkroniserad",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Namnet ska börja med ett alfabet eller _",
"followingCharactersAreNotAllowed": "Följande tecken är inte tillåtna",
"columnNameRequired": "Kolumnnamn krävs",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Projektnamnet har mer än 50 tecken",
"projectNameCannotStartWithSpace": "Projektnamnet kan inte börja med ett mellanslag",
"requiredField": "Obligatoriskt fält",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "โครงการ",
@ -98,7 +99,8 @@
"gallery": "แกลลอร",
"form": "รปราง",
"kanban": "ภาษาคณาตา",
"calendar": "ปฏน"
"calendar": "ปฏน",
"map": "Map"
},
"user": "ผใช",
"users": "ผใช",
@ -136,6 +138,7 @@
"Currency": "สกลเงน",
"Percent": "รอยละ",
"Duration": "ระยะเวลา",
"GeoData": "GeoData",
"Rating": "การจดอนดบ",
"Formula": "สตร",
"Rollup": "มวน",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "ฟงกนรวม",
"dbCreateIfNotExists": "ฐานขอมล: สรางถาไมอย",
"clientKey": "รหสลกคา",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "แถวตอหนา",
"upload": "เลอกไฟลจะอปโหลด",
"upload_sub": "หรอลากและวางไฟล",
@ -600,6 +615,7 @@
"gallery": "เพมมมมองแกลลอร",
"form": "เพมมมมองแบบฟอรม",
"kanban": "เพมมมมอง Kanban",
"map": "Add Map View",
"calendar": "เพมมมมองปฏน"
},
"tablesMetadataInSync": "ตารางเมตาดาตากำลงซงค",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

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

@ -74,7 +74,8 @@
"insertBefore": "Önce Ekle",
"hideField": "Alanı Gizle",
"sortAsc": "Artan Sırala",
"sortDesc": "Azalan Sıralama"
"sortDesc": "Azalan Sıralama",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Proje",
@ -98,7 +99,8 @@
"gallery": "Galeri",
"form": "Form",
"kanban": "Kanban",
"calendar": "Takvim"
"calendar": "Takvim",
"map": "Map"
},
"user": "Kullanıcı",
"users": "Kullanıcılar",
@ -136,6 +138,7 @@
"Currency": "Para birimi",
"Percent": "Yüzde",
"Duration": "Süre",
"GeoData": "GeoData",
"Rating": "Değerlendirme",
"Formula": "Formül",
"Rollup": "Referans hesapla",
@ -253,6 +256,9 @@
"barcodeFormat": "Barkod formatı",
"qrCodeValueTooLong": "QR kodu için çok fazla karakter",
"barcodeValueTooLong": "Barkod için çok fazla karakter",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Birleştirme fonksiyonu",
"dbCreateIfNotExists": "Veritabanı : yoksa oluştur",
"clientKey": "İstemci Anahtarı",
@ -452,6 +458,10 @@
"stackedBy": "Tarafından Yığılmış",
"chooseGroupingField": "Bir Gruplama Alanı Seçin",
"addOrEditStack": "Yığın Ekle / Düzenle"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "İçerik oluşturucu yeni projeler oluşturabilir ve davet edilen herhangi bir projeye erişebilir.",
"orgViewer": "İzleyicinin yeni proje oluşturmasına izin verilmez ancak davet edilen herhangi bir projeye erişebilir."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Sayfa başına satır",
"upload": "Yüklenecek Dosyayı Seçin",
"upload_sub": "veya dosyayı sürükleyip bırakın",
@ -600,6 +615,7 @@
"gallery": "Galeri görünümü ekle",
"form": "Form görünümü ekle",
"kanban": "Kanban görünümü ekle",
"map": "Add Map View",
"calendar": "Takvim görünümü ekle"
},
"tablesMetadataInSync": "Tablonun meta verileri senkronize",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "İsim bir alfabe veya _ ile başlamalıdır",
"followingCharactersAreNotAllowed": "Aşağıdaki karakterlere izin verilmez",
"columnNameRequired": "Sütun adı gereklidir",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Proje adı 50 karakteri aşıyor",
"projectNameCannotStartWithSpace": "Proje adı boşlukla başlayamaz",
"requiredField": "Zorunlu alan",

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

@ -74,7 +74,8 @@
"insertBefore": "Вставити перед",
"hideField": "Приховати поле",
"sortAsc": "За зростанням",
"sortDesc": "За спаданням"
"sortDesc": "За спаданням",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Проєкт",
@ -98,7 +99,8 @@
"gallery": "Галерея",
"form": "Форма",
"kanban": "Канбан",
"calendar": "Календар"
"calendar": "Календар",
"map": "Map"
},
"user": "Користувач",
"users": "Користувачі",
@ -136,6 +138,7 @@
"Currency": "Валюта",
"Percent": "Відсоток",
"Duration": "Тривалість",
"GeoData": "GeoData",
"Rating": "Рейтинг",
"Formula": "Формула",
"Rollup": "Накопичення",
@ -253,6 +256,9 @@
"barcodeFormat": "Формат штрих-коду",
"qrCodeValueTooLong": "Забагато символів для QR-коду",
"barcodeValueTooLong": "Забагато символів для штрих-коду",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Агрегатна функція",
"dbCreateIfNotExists": "База даних: створити, якщо не існує",
"clientKey": "Ключ клієнта",
@ -452,6 +458,10 @@
"stackedBy": "Групувати по",
"chooseGroupingField": "Виберіть поле групування",
"addOrEditStack": "Додавання/Редагування Стеку"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "Розробник може створювати нові проєкти та мати доступ до будь-якого відкритого проєкту.",
"orgViewer": "Глядач не може створювати нові проєкти, але він може отримати доступ до будь-якого відкритого проєкту."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "Рядків на сторінці",
"upload": "Виберіть файл для завантаження",
"upload_sub": "або перетягніть файл",
@ -600,6 +615,7 @@
"gallery": "Додати вигляд галереї",
"form": "Додати вигляд форми",
"kanban": "Додати вигляд Kanban",
"map": "Add Map View",
"calendar": "Додати вигляд календаря"
},
"tablesMetadataInSync": "Таблиці метаданих синхронізуються",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Ім'я повинно починатися з літери або _",
"followingCharactersAreNotAllowed": "Наступні символи не допускаються",
"columnNameRequired": "Ім'я стовпця є обов'язковим",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Назва проєкту перевищує 50 символів",
"projectNameCannotStartWithSpace": "Назва проєкту не може починатися з пробілу",
"requiredField": "Обов'язкове поле",

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

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "Dự định",
@ -98,7 +99,8 @@
"gallery": "Bộ sưu tập",
"form": "Mẫu đơn",
"kanban": "Kanban.",
"calendar": "Lịch"
"calendar": "Lịch",
"map": "Map"
},
"user": "Người dùng",
"users": "Người dùng",
@ -136,6 +138,7 @@
"Currency": "Tiền tệ",
"Percent": "Phần trăm",
"Duration": "Khoảng thời gian",
"GeoData": "GeoData",
"Rating": "Xếp hạng",
"Formula": "Công thức",
"Rollup": "ROLLUP.",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "Chức năng tổng hợp",
"dbCreateIfNotExists": "Cơ sở dữ liệu: Tạo nếu không tồn tại",
"clientKey": "Khóa khách",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"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."
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"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",
@ -600,6 +615,7 @@
"gallery": "Thêm Gallery View.",
"form": "Thêm hình thức xem",
"kanban": "Thêm tầm nhìn Kanban",
"map": "Add Map View",
"calendar": "Thêm chế độ xem lịch"
},
"tablesMetadataInSync": "Bảng siêu dữ liệu được đồng bộ hóa",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",

53
packages/nc-gui/lang/zh-Hans.json

@ -67,14 +67,15 @@
"questions": "问题",
"reachOut": "联系我们",
"betaNote": "此功能仍在测试中。",
"moreInfo": "点击此处了解更多信息。",
"moreInfo": "这里可以找到更多信息",
"logs": "日志",
"groupingField": "分组字段",
"insertAfter": "在右侧插入列",
"insertBefore": "在左侧插入列",
"hideField": "隐藏字段",
"sortAsc": "升序",
"sortDesc": "降序"
"sortDesc": "降序",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "项目",
@ -98,7 +99,8 @@
"gallery": "画廊",
"form": "表单",
"kanban": "看板",
"calendar": "日历"
"calendar": "日历",
"map": "Map"
},
"user": "用户",
"users": "用户",
@ -136,6 +138,7 @@
"Currency": "货币",
"Percent": "百分比",
"Duration": "时长",
"GeoData": "GeoData",
"Rating": "评分",
"Formula": "公式",
"Rollup": "聚合",
@ -253,6 +256,9 @@
"barcodeFormat": "条形码码制",
"qrCodeValueTooLong": "字数超出二维码容量",
"barcodeValueTooLong": "字数超出条形码容量",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "汇总功能",
"dbCreateIfNotExists": "自动创建数据库",
"clientKey": "客户端密钥",
@ -298,7 +304,7 @@
"noData": "暂无数据",
"goToDashboard": "转到仪表板",
"importing": "导入中",
"flattenNested": "Flatten Nested",
"flattenNested": "扁平化嵌套",
"downloadAllowed": "允许下载",
"weAreHiring": "我们在招募!",
"primaryKey": "主键",
@ -380,14 +386,14 @@
"renameTable": "重命名表格",
"deleteTable": "删除表格",
"addField": "添加新字段",
"setDisplay": "Set as Display value",
"setDisplay": "设置为显示值",
"addRow": "添加新行",
"saveRow": "保存行",
"saveAndExit": "保存并退出",
"saveAndStay": "保存并留在此页",
"insertRow": "插入新行",
"deleteRow": "删除行",
"duplicateRow": "Duplicate Row",
"duplicateRow": "复制该行",
"deleteSelectedRow": "删除所选行",
"importExcel": "导入 Excel",
"importCSV": "导入 CSV",
@ -452,6 +458,10 @@
"stackedBy": "分类依据为",
"chooseGroupingField": "选择分组字段",
"addOrEditStack": "添加/编辑分类标签"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -508,16 +518,21 @@
"renderError": "条形码错误 - 请注意输入数据和条形码类型之间的兼容性"
},
"nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."
"computedFieldUnableToClear": "警告:自动计算的字段无法被清除内容。",
"qrFieldsCannotBeDirectlyChanged": "警告:二维码字段无法更改。"
}
},
"info": {
"pasteNotSupported": "Paste operation is not supported on the active cell",
"pasteNotSupported": "处于活动状态的单元格不能粘贴内容",
"roles": {
"orgCreator": "创始人可以创建新项目,访问受邀项目。",
"orgViewer": "游客不能创建新项目,仅允许访问受邀项目。"
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "每页行驶",
"upload": "选择文件以上传",
"upload_sub": "或拖放文件",
@ -600,6 +615,7 @@
"gallery": "添加画廊视图",
"form": "添加表单视图",
"kanban": "添加看板视图",
"map": "Add Map View",
"calendar": "添加日历视图"
},
"tablesMetadataInSync": "表元数据同步",
@ -627,18 +643,18 @@
"valueAlreadyInList": "此值已经在列表中",
"noColumnsToUpdate": "没有要更新的列",
"tableDeleted": "已成功删除表",
"generatePublicShareableReadonlyBase": "Generate publicly shareable readonly base",
"generatePublicShareableReadonlyBase": "生成公开可共享的只读库",
"deleteViewConfirmation": "您确定要删除此视图?",
"deleteTableConfirmation": "您想要删除该表吗?",
"showM2mTables": "显示中间表",
"showM2mTablesDesc": "Many-to-many relation is supported via a junction table & is hidden by default. Enable this option to list all such tables along with existing tables.",
"showNullInCells": "Show NULL in Cells",
"showNullInCellsDesc": "Display 'NULL' tag in cells holding NULL value. This helps differentiate against cells holding EMPTY string.",
"showNullAndEmptyInFilter": "Show NULL and EMPTY in Filter",
"showNullAndEmptyInFilterDesc": "Enable 'additional' filters to differentiate fields containing NULL & Empty Strings. Default support for Blank treats both NULL & Empty strings alike.",
"showM2mTablesDesc": "NocoDB 用来支持多对多关系的中间表默认不可见。打开此选项将显示所有中间表。",
"showNullInCells": "单元格为空时显示 NULL",
"showNullInCellsDesc": "未设置值的单元格将显示 NULL。这是为了和值为空字符串(显示 EMPTY)的单元格作出区分。",
"showNullAndEmptyInFilter": "在过滤器中显示 NULL 和 EMPTY 选项",
"showNullAndEmptyInFilterDesc": "启用”附加”过滤器来区分未设置值(NULL)和值为空字符串(EMPTY)的字段。默认对 NULL 的处理和对空字符串的处理一样。",
"deleteKanbanStackConfirmation": "删除这个类别标签也将从 \"{groupingField}\"中删除选择选项 \"{stackToBeDeleted}\"。这类记录将移到未分类的类别中。",
"computedFieldEditWarning": "Computed field: contents are read-only. Use column edit menu to reconfigure",
"computedFieldDeleteWarning": "Computed field: contents are read-only. Unable to clear content.",
"computedFieldEditWarning": "计算字段:此内容是只读的。使用列编辑菜单来重新配置该字段",
"computedFieldDeleteWarning": "计算字段:此内容是只读的。无法清除内容。",
"noMoreRecords": "暂无数据"
},
"error": {
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "名称应该以字母或 _ 开头",
"followingCharactersAreNotAllowed": "不允许使用以下字符",
"columnNameRequired": "列名是必填项",
"columnNameExceedsCharacters": "列名的长度超过了 {value} 字符的限制",
"projectNameExceeds50Characters": "项目名称超过 50 个字符",
"projectNameCannotStartWithSpace": "项目名称不能以空格开头",
"requiredField": "必填字段",
@ -722,7 +739,7 @@
},
"success": {
"columnDuplicated": "此列的副本创建成功",
"rowDuplicatedWithoutSavedYet": "Row duplicated (not saved)",
"rowDuplicatedWithoutSavedYet": "该行已复制(未保存)",
"updatedUIACL": "已成功更新表的 UI ACL",
"pluginUninstalled": "插件卸载成功",
"pluginSettingsSaved": "插件设置保存成功",

21
packages/nc-gui/lang/zh-Hant.json

@ -74,7 +74,8 @@
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
"sortDesc": "Sort Descending",
"geoDataField": "GeoData Field"
},
"objects": {
"project": "項目",
@ -98,7 +99,8 @@
"gallery": "相簿",
"form": "表單",
"kanban": "看板",
"calendar": "日曆"
"calendar": "日曆",
"map": "Map"
},
"user": "使用者",
"users": "使用者",
@ -136,6 +138,7 @@
"Currency": "貨幣",
"Percent": "百分",
"Duration": "期間",
"GeoData": "GeoData",
"Rating": "評分",
"Formula": "公式",
"Rollup": "捲起",
@ -253,6 +256,9 @@
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"yourLocation": "Your Location",
"lng": "Lng",
"lat": "Lat",
"aggregateFunction": "匯總功能",
"dbCreateIfNotExists": "資料庫:不存在則建立",
"clientKey": "用戶端金鑰",
@ -452,6 +458,10 @@
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"
},
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field"
}
},
"tooltip": {
@ -518,6 +528,11 @@
"orgCreator": "建立者可以建立專案與存取任何受邀請的專案",
"orgViewer": "檢視者不能建立專案但可以存取任何受邀請的專案"
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
},
"footerInfo": "每頁行駛",
"upload": "選擇檔案以上傳",
"upload_sub": "或拖放檔案",
@ -600,6 +615,7 @@
"gallery": "加入相簿檢視",
"form": "加入表單檢視",
"kanban": "加入看板檢視",
"map": "Add Map View",
"calendar": "加入日曆檢視"
},
"tablesMetadataInSync": "表元數據同步",
@ -690,6 +706,7 @@
"nameShouldStartWithAnAlphabetOr_": "名稱必須用 英文字母 或 _ 當開頭",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "欄位名稱必填",
"columnNameExceedsCharacters": "The length of column name exceeds the max {value} characters",
"projectNameExceeds50Characters": "專案名稱超過 50 個字元",
"projectNameCannotStartWithSpace": "專案名稱不能有空白開頭",
"requiredField": "必填欄位",

1
packages/nc-gui/lib/constants.ts

@ -57,6 +57,7 @@ export const rolePermissions = {
fieldsSync: true,
gridColUpdate: true,
filterSync: true,
filterChildrenRead: true,
csvImport: true,
apiDocs: true,
projectSettings: true,

1
packages/nc-gui/lib/enums.ts

@ -88,6 +88,7 @@ export enum SmartsheetStoreEvents {
DATA_RELOAD = 'data-reload',
FIELD_RELOAD = 'field-reload',
FIELD_ADD = 'field-add',
MAPPED_BY_COLUMN_CHANGE = 'mapped-by-column-change',
}
export enum DataSourcesSubTab {

1
packages/nc-gui/lib/types.ts

@ -32,6 +32,7 @@ export interface Field {
title: string
fk_column_id?: string
system?: boolean
isViewEssentialField?: boolean
}
export type Roles<T extends Role | ProjectRole = Role | ProjectRole> = Record<T | string, boolean>

116
packages/nc-gui/package-lock.json generated

@ -27,6 +27,8 @@
"jsep": "^1.3.6",
"just-clone": "^6.1.1",
"jwt-decode": "^3.1.2",
"leaflet": "^1.9.2",
"leaflet.markercluster": "^1.5.3",
"locale-codes": "^1.3.1",
"monaco-editor": "^0.33.0",
"nocodb-sdk": "file:../nocodb-sdk",
@ -43,6 +45,7 @@
"vue-github-button": "^3.0.3",
"vue-i18n": "^9.2.2",
"vue-qrcode-reader": "3.1.0-vue3-compatibility.2",
"vue3-contextmenu": "^0.2.12",
"vue3-text-clamp": "^0.1.1",
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5"
@ -71,6 +74,9 @@
"@nuxt/image-edge": "^1.0.0-27657146.da85542",
"@types/axios": "^0.14.0",
"@types/dagre": "^0.7.48",
"@types/file-saver": "^2.0.5",
"@types/leaflet": "^1.9.0",
"@types/leaflet.markercluster": "^1.5.1",
"@types/papaparse": "^5.3.2",
"@types/qrcode": "^1.5.0",
"@types/sortablejs": "^1.13.0",
@ -101,7 +107,7 @@
}
},
"../nocodb-sdk": {
"version": "0.105.2",
"version": "0.105.3",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -3135,7 +3141,8 @@
"node_modules/@types/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ=="
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==",
"dev": true
},
"node_modules/@types/form-data": {
"version": "0.0.33",
@ -3146,6 +3153,12 @@
"@types/node": "*"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -3158,6 +3171,24 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/leaflet": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.0.tgz",
"integrity": "sha512-7LeOSj7EloC5UcyOMo+1kc3S1UT3MjJxwqsMT1d2PTyvQz53w0Y0oSSk9nwZnOZubCmBvpSNGceucxiq+ZPEUw==",
"dev": true,
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/leaflet.markercluster": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.5.1.tgz",
"integrity": "sha512-gzJzP10qO6Zkts5QNVmSAEDLYicQHTEBLT9HZpFrJiSww9eDAs5OWHvIskldf41MvDv1gbMukuEBQEawHn+wtA==",
"dev": true,
"dependencies": {
"@types/leaflet": "*"
}
},
"node_modules/@types/mdast": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@ -10860,6 +10891,19 @@
"safe-buffer": "~5.1.0"
}
},
"node_modules/leaflet": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
"integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ=="
},
"node_modules/leaflet.markercluster": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz",
"integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==",
"peerDependencies": {
"leaflet": "^1.3.1"
}
},
"node_modules/less": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
@ -11473,6 +11517,11 @@
"minipass": "^2.9.0"
}
},
"node_modules/mitt": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
},
"node_modules/mkdir": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/mkdir/-/mkdir-0.0.2.tgz",
@ -17496,6 +17545,16 @@
"node": ">=0.10.0"
}
},
"node_modules/vue3-contextmenu": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/vue3-contextmenu/-/vue3-contextmenu-0.2.12.tgz",
"integrity": "sha512-lSA+Pq4wozbf9nDmMBvz2Z2DXwGwCEeXEDsmQi+cIxc5FzPa1Ia013hv5/DQZwr4dPilNxrOXPht1u0CMXcpVw==",
"dependencies": {
"core-js": "^3.6.5",
"mitt": "^2.1.0",
"vue": "^3.0.0"
}
},
"node_modules/vue3-text-clamp": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/vue3-text-clamp/-/vue3-text-clamp-0.1.1.tgz",
@ -20398,7 +20457,8 @@
"@types/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ=="
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==",
"dev": true
},
"@types/form-data": {
"version": "0.0.33",
@ -20409,6 +20469,12 @@
"@types/node": "*"
}
},
"@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"dev": true
},
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -20421,6 +20487,24 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"@types/leaflet": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.0.tgz",
"integrity": "sha512-7LeOSj7EloC5UcyOMo+1kc3S1UT3MjJxwqsMT1d2PTyvQz53w0Y0oSSk9nwZnOZubCmBvpSNGceucxiq+ZPEUw==",
"dev": true,
"requires": {
"@types/geojson": "*"
}
},
"@types/leaflet.markercluster": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.5.1.tgz",
"integrity": "sha512-gzJzP10qO6Zkts5QNVmSAEDLYicQHTEBLT9HZpFrJiSww9eDAs5OWHvIskldf41MvDv1gbMukuEBQEawHn+wtA==",
"dev": true,
"requires": {
"@types/leaflet": "*"
}
},
"@types/mdast": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@ -26005,6 +26089,17 @@
}
}
},
"leaflet": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
"integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ=="
},
"leaflet.markercluster": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz",
"integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==",
"requires": {}
},
"less": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
@ -26498,6 +26593,11 @@
"minipass": "^2.9.0"
}
},
"mitt": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
},
"mkdir": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/mkdir/-/mkdir-0.0.2.tgz",
@ -30808,6 +30908,16 @@
"is-plain-object": "3.0.1"
}
},
"vue3-contextmenu": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/vue3-contextmenu/-/vue3-contextmenu-0.2.12.tgz",
"integrity": "sha512-lSA+Pq4wozbf9nDmMBvz2Z2DXwGwCEeXEDsmQi+cIxc5FzPa1Ia013hv5/DQZwr4dPilNxrOXPht1u0CMXcpVw==",
"requires": {
"core-js": "^3.6.5",
"mitt": "^2.1.0",
"vue": "^3.0.0"
}
},
"vue3-text-clamp": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/vue3-text-clamp/-/vue3-text-clamp-0.1.1.tgz",

6
packages/nc-gui/package.json

@ -50,6 +50,8 @@
"jsep": "^1.3.6",
"just-clone": "^6.1.1",
"jwt-decode": "^3.1.2",
"leaflet": "^1.9.2",
"leaflet.markercluster": "^1.5.3",
"locale-codes": "^1.3.1",
"monaco-editor": "^0.33.0",
"nocodb-sdk": "file:../nocodb-sdk",
@ -66,6 +68,7 @@
"vue-github-button": "^3.0.3",
"vue-i18n": "^9.2.2",
"vue-qrcode-reader": "3.1.0-vue3-compatibility.2",
"vue3-contextmenu": "^0.2.12",
"vue3-text-clamp": "^0.1.1",
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5"
@ -94,6 +97,9 @@
"@nuxt/image-edge": "^1.0.0-27657146.da85542",
"@types/axios": "^0.14.0",
"@types/dagre": "^0.7.48",
"@types/file-saver": "^2.0.5",
"@types/leaflet": "^1.9.0",
"@types/leaflet.markercluster": "^1.5.1",
"@types/papaparse": "^5.3.2",
"@types/qrcode": "^1.5.0",
"@types/sortablejs": "^1.13.0",

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

@ -1,5 +1,6 @@
<script lang="ts" setup>
import type { UploadChangeParam, UploadFile } from 'ant-design-vue'
import type { BaseType } from 'nocodb-sdk'
import {
message,
ref,
@ -98,6 +99,7 @@ function openQuickImportDialog(type: QuickImportTypes, file: File) {
'modelValue': isOpen,
'importType': type,
'onUpdate:modelValue': closeDialog,
'baseId': bases.value?.filter((base: BaseType) => base.enabled)[0].id,
})
vNode.value?.component?.exposed?.handleChange({
@ -128,7 +130,7 @@ function openCreateTable() {
const { close } = useDialog(resolveComponent('DlgTableCreate'), {
'modelValue': isOpen,
'onUpdate:modelValue': closeDialog,
'baseId': bases.value[0].id,
'baseId': bases.value?.filter((base: BaseType) => base.enabled)[0].id,
})
function closeDialog() {

35
packages/nc-gui/pages/[projectType]/map/[viewId]/index.vue

@ -0,0 +1,35 @@
<script setup lang="ts">
import { message } from 'ant-design-vue'
import { definePageMeta } from '#imports'
definePageMeta({
public: true,
requiresAuth: false,
layout: 'shared-view',
})
const route = useRoute()
const { loadSharedView } = useSharedView()
const showPassword = ref(false)
try {
await loadSharedView(route.params.viewId as string)
} catch (e: any) {
if (e?.response?.status === 403) {
showPassword.value = true
} else {
message.error(await extractSdkResponseErrorMsg(e))
}
}
</script>
<template>
<NuxtLayout class="flex" name="shared-view">
<div v-if="showPassword">
<LazySharedViewAskPassword v-model="showPassword" />
</div>
<LazySharedViewMap v-else />
</NuxtLayout>
</template>

9
packages/nc-gui/pages/index/index/create.vue

@ -37,9 +37,13 @@ const formState = reactive({
title: '',
})
const creating = ref(false)
const createProject = async () => {
$e('a:project:create:xcdb')
try {
creating.value = true
const result = await api.project.create({
title: formState.title,
})
@ -47,6 +51,8 @@ const createProject = async () => {
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
} finally {
creating.value = false
}
}
@ -90,7 +96,8 @@ onMounted(async () => {
</a-form-item>
<div class="text-center">
<button class="scaling-btn bg-opacity-100" type="submit">
<a-spin v-if="creating" spinning />
<button v-else class="scaling-btn bg-opacity-100" type="submit">
<span class="flex items-center gap-2">
<MaterialSymbolsRocketLaunchOutline />
{{ $t('general.create') }}

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

@ -27,6 +27,7 @@ export const isCurrency = (column: ColumnType) => column.uidt === UITypes.Curren
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 isGeoData = (column: ColumnType) => column.uidt === UITypes.GeoData
export const isPercent = (column: ColumnType) => column.uidt === UITypes.Percent
export const isSpecificDBType = (column: ColumnType) => column.uidt === UITypes.SpecificDBType
export const isAutoSaved = (column: ColumnType) =>
@ -43,6 +44,7 @@ export const isAutoSaved = (column: ColumnType) =>
UITypes.AutoNumber,
UITypes.SpecificDBType,
UITypes.Geometry,
UITypes.GeoData,
UITypes.Duration,
].includes(column.uidt as UITypes)

6
packages/nc-gui/utils/columnUtils.ts

@ -8,6 +8,7 @@ import TextSubject from '~icons/mdi/text-subject'
import JSONIcon from '~icons/mdi/code-json'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
import Attachment from '~icons/mdi/attachment'
import Marker from '~icons/mdi/map-marker'
import CheckboxMarkedOutline from '~icons/mdi/checkbox-marked-outline'
import FormatListBulletedSquare from '~icons/mdi/format-list-bulleted-square'
import ArrowDownDropCircle from '~icons/mdi/arrow-down-drop-circle'
@ -141,6 +142,11 @@ const uiTypes = [
name: UITypes.Geometry,
icon: RulerSquareCompass,
},
{
name: UITypes.GeoData,
icon: Marker,
},
{
name: UITypes.JSON,
icon: JSONIcon,

3
packages/nc-gui/utils/geoDataUtils.ts

@ -0,0 +1,3 @@
const latLongToJoinedString = (lat: number, long: number) => `${lat.toFixed(7)};${long.toFixed(7)}`
export { latLongToJoinedString }

1
packages/nc-gui/utils/index.ts

@ -20,4 +20,5 @@ export * from './userUtils'
export * from './stringUtils'
export * from './memStorage'
export * from './browserUtils'
export * from './geoDataUtils'
export * from './mimeTypeUtils'

693
packages/nc-gui/utils/mimeTypeUtils.ts

@ -1,694 +1,3 @@
const mimeTypes: Record<string, string> = {
'123': 'application/vnd.lotus-1-2-3',
'x3d': 'application/vnd.hzn-3d-crossword',
'3gp': 'video/3gpp',
'3g2': 'video/3gpp2',
'mseq': 'application/vnd.mseq',
'pwn': 'application/vnd.3m.post-it-notes',
'plb': 'application/vnd.3gpp.pic-bw-large',
'psb': 'application/vnd.3gpp.pic-bw-small',
'pvb': 'application/vnd.3gpp.pic-bw-var',
'tcap': 'application/vnd.3gpp2.tcap',
'7z': 'application/x-7z-compressed',
'abw': 'application/x-abiword',
'ace': 'application/x-ace-compressed',
'acc': 'application/vnd.americandynamics.acc',
'acu': 'application/vnd.acucobol',
'atc': 'application/vnd.acucorp',
'adp': 'audio/adpcm',
'aab': 'application/x-authorware-bin',
'aam': 'application/x-authorware-map',
'aas': 'application/x-authorware-seg',
'air': 'application/vnd.adobe.air-application-installer-package+zip',
'swf': 'application/x-shockwave-flash',
'fxp': 'application/vnd.adobe.fxp',
'pdf': 'application/pdf',
'ppd': 'application/vnd.cups-ppd',
'dir': 'application/x-director',
'xdp': 'application/vnd.adobe.xdp+xml',
'xfdf': 'application/vnd.adobe.xfdf',
'aac': 'audio/x-aac',
'ahead': 'application/vnd.ahead.space',
'azf': 'application/vnd.airzip.filesecure.azf',
'azs': 'application/vnd.airzip.filesecure.azs',
'azw': 'application/vnd.amazon.ebook',
'ami': 'application/vnd.amiga.ami',
'/A': 'application/andrew-inset',
'apk': 'application/vnd.android.package-archive',
'cii': 'application/vnd.anser-web-certificate-issue-initiation',
'fti': 'application/vnd.anser-web-funds-transfer-initiation',
'atx': 'application/vnd.antix.game-component',
'dmg': 'application/x-apple-diskimage',
'mpkg': 'application/vnd.apple.installer+xml',
'aw': 'application/applixware',
'les': 'application/vnd.hhe.lesson-player',
'swi': 'application/vnd.aristanetworks.swi',
's': 'text/x-asm',
'atomcat': 'application/atomcat+xml',
'atomsvc': 'application/atomsvc+xml',
'atom, .xml': 'application/atom+xml',
'ac': 'application/pkix-attr-cert',
'aif': 'audio/x-aiff',
'avi': 'video/x-msvideo',
'aep': 'application/vnd.audiograph',
'dxf': 'image/vnd.dxf',
'dwf': 'model/vnd.dwf',
'par': 'text/plain-bas',
'bcpio': 'application/x-bcpio',
'bin': 'application/octet-stream',
'bmp': 'image/bmp',
'torrent': 'application/x-bittorrent',
'cod': 'application/vnd.rim.cod',
'mpm': 'application/vnd.blueice.multipass',
'bmi': 'application/vnd.bmi',
'sh': 'application/x-sh',
'btif': 'image/prs.btif',
'rep': 'application/vnd.businessobjects',
'bz': 'application/x-bzip',
'bz2': 'application/x-bzip2',
'csh': 'application/x-csh',
'c': 'text/x-c',
'cdxml': 'application/vnd.chemdraw+xml',
'css': 'text/css',
'cdx': 'chemical/x-cdx',
'cml': 'chemical/x-cml',
'csml': 'chemical/x-csml',
'cdbcmsg': 'application/vnd.contact.cmsg',
'cla': 'application/vnd.claymore',
'c4g': 'application/vnd.clonk.c4group',
'sub': 'image/vnd.dvb.subtitle',
'cdmia': 'application/cdmi-capability',
'cdmic': 'application/cdmi-container',
'cdmid': 'application/cdmi-domain',
'cdmio': 'application/cdmi-object',
'cdmiq': 'application/cdmi-queue',
'c11amc': 'application/vnd.cluetrust.cartomobile-config',
'c11amz': 'application/vnd.cluetrust.cartomobile-config-pkg',
'ras': 'image/x-cmu-raster',
'dae': 'model/vnd.collada+xml',
'csv': 'text/csv',
'cpt': 'application/mac-compactpro',
'wmlc': 'application/vnd.wap.wmlc',
'cgm': 'image/cgm',
'ice': 'x-conference/x-cooltalk',
'cmx': 'image/x-cmx',
'xar': 'application/vnd.xara',
'cmc': 'application/vnd.cosmocaller',
'cpio': 'application/x-cpio',
'clkx': 'application/vnd.crick.clicker',
'clkk': 'application/vnd.crick.clicker.keyboard',
'clkp': 'application/vnd.crick.clicker.palette',
'clkt': 'application/vnd.crick.clicker.template',
'clkw': 'application/vnd.crick.clicker.wordbank',
'wbs': 'application/vnd.criticaltools.wbs+xml',
'cryptonote': 'application/vnd.rig.cryptonote',
'cif': 'chemical/x-cif',
'cmdf': 'chemical/x-cmdf',
'cu': 'application/cu-seeme',
'cww': 'application/prs.cww',
'curl': 'text/vnd.curl',
'dcurl': 'text/vnd.curl.dcurl',
'mcurl': 'text/vnd.curl.mcurl',
'scurl': 'text/vnd.curl.scurl',
'car': 'application/vnd.curl.car',
'pcurl': 'application/vnd.curl.pcurl',
'cmp': 'application/vnd.yellowriver-custom-menu',
'dssc': 'application/dssc+der',
'xdssc': 'application/dssc+xml',
'deb': 'application/x-debian-package',
'uva': 'audio/vnd.dece.audio',
'uvi': 'image/vnd.dece.graphic',
'uvh': 'video/vnd.dece.hd',
'uvm': 'video/vnd.dece.mobile',
'uvu': 'video/vnd.uvvu.mp4',
'uvp': 'video/vnd.dece.pd',
'uvs': 'video/vnd.dece.sd',
'uvv': 'video/vnd.dece.video',
'dvi': 'application/x-dvi',
'seed': 'application/vnd.fdsn.seed',
'dtb': 'application/x-dtbook+xml',
'res': 'application/x-dtbresource+xml',
'ait': 'application/vnd.dvb.ait',
'svc': 'application/vnd.dvb.service',
'eol': 'audio/vnd.digital-winds',
'djvu': 'image/vnd.djvu',
'dtd': 'application/xml-dtd',
'mlp': 'application/vnd.dolby.mlp',
'wad': 'application/x-doom',
'dpg': 'application/vnd.dpgraph',
'dra': 'audio/vnd.dra',
'dfac': 'application/vnd.dreamfactory',
'dts': 'audio/vnd.dts',
'dtshd': 'audio/vnd.dts.hd',
'dwg': 'image/vnd.dwg',
'geo': 'application/vnd.dynageo',
'es': 'application/ecmascript',
'mag': 'application/vnd.ecowin.chart',
'mmr': 'image/vnd.fujixerox.edmics-mmr',
'rlc': 'image/vnd.fujixerox.edmics-rlc',
'exi': 'application/exi',
'mgz': 'application/vnd.proteus.magazine',
'epub': 'application/epub+zip',
'eml': 'message/rfc822',
'nml': 'application/vnd.enliven',
'xpr': 'application/vnd.is-xpr',
'xif': 'image/vnd.xiff',
'xfdl': 'application/vnd.xfdl',
'emma': 'application/emma+xml',
'ez2': 'application/vnd.ezpix-album',
'ez3': 'application/vnd.ezpix-package',
'fst': 'image/vnd.fst',
'fvt': 'video/vnd.fvt',
'fbs': 'image/vnd.fastbidsheet',
'fe_launch': 'application/vnd.denovo.fcselayout-link',
'f4v': 'video/x-f4v',
'flv': 'video/x-flv',
'fpx': 'image/vnd.fpx',
'npx': 'image/vnd.net-fpx',
'flx': 'text/vnd.fmi.flexstor',
'fli': 'video/x-fli',
'ftc': 'application/vnd.fluxtime.clip',
'fdf': 'application/vnd.fdf',
'f': 'text/x-fortran',
'mif': 'application/vnd.mif',
'fm': 'application/vnd.framemaker',
'fh': 'image/x-freehand',
'fsc': 'application/vnd.fsc.weblaunch',
'fnc': 'application/vnd.frogans.fnc',
'ltf': 'application/vnd.frogans.ltf',
'ddd': 'application/vnd.fujixerox.ddd',
'xdw': 'application/vnd.fujixerox.docuworks',
'xbd': 'application/vnd.fujixerox.docuworks.binder',
'oas': 'application/vnd.fujitsu.oasys',
'oa2': 'application/vnd.fujitsu.oasys2',
'oa3': 'application/vnd.fujitsu.oasys3',
'fg5': 'application/vnd.fujitsu.oasysgp',
'bh2': 'application/vnd.fujitsu.oasysprs',
'spl': 'application/x-futuresplash',
'fzs': 'application/vnd.fuzzysheet',
'g3': 'image/g3fax',
'gmx': 'application/vnd.gmx',
'gtw': 'model/vnd.gtw',
'txd': 'application/vnd.genomatix.tuxedo',
'ggb': 'application/vnd.geogebra.file',
'ggt': 'application/vnd.geogebra.tool',
'gdl': 'model/vnd.gdl',
'gex': 'application/vnd.geometry-explorer',
'gxt': 'application/vnd.geonext',
'g2w': 'application/vnd.geoplan',
'g3w': 'application/vnd.geospace',
'gsf': 'application/x-font-ghostscript',
'bdf': 'application/x-font-bdf',
'gtar': 'application/x-gtar',
'texinfo': 'application/x-texinfo',
'gnumeric': 'application/x-gnumeric',
'kml': 'application/vnd.google-earth.kml+xml',
'kmz': 'application/vnd.google-earth.kmz',
'gpx': 'application/gpx+xml',
'gqf': 'application/vnd.grafeq',
'gif': 'image/gif',
'gv': 'text/vnd.graphviz',
'gac': 'application/vnd.groove-account',
'ghf': 'application/vnd.groove-help',
'gim': 'application/vnd.groove-identity-message',
'grv': 'application/vnd.groove-injector',
'gtm': 'application/vnd.groove-tool-message',
'tpl': 'application/vnd.groove-tool-template',
'vcg': 'application/vnd.groove-vcard',
'h261': 'video/h261',
'h263': 'video/h263',
'h264': 'video/h264',
'hpid': 'application/vnd.hp-hpid',
'hps': 'application/vnd.hp-hps',
'hdf': 'application/x-hdf',
'rip': 'audio/vnd.rip',
'hbci': 'application/vnd.hbci',
'jlt': 'application/vnd.hp-jlyt',
'pcl': 'application/vnd.hp-pcl',
'hpgl': 'application/vnd.hp-hpgl',
'hvs': 'application/vnd.yamaha.hv-script',
'hvd': 'application/vnd.yamaha.hv-dic',
'hvp': 'application/vnd.yamaha.hv-voice',
'sfd-hdstx': 'application/vnd.hydrostatix.sof-data',
'stk': 'application/hyperstudio',
'hal': 'application/vnd.hal+xml',
'html': 'text/html',
'irm': 'application/vnd.ibm.rights-management',
'sc': 'application/vnd.ibm.secure-container',
'ics': 'text/calendar',
'icc': 'application/vnd.iccprofile',
'ico': 'image/x-icon',
'igl': 'application/vnd.igloader',
'ief': 'image/ief',
'ivp': 'application/vnd.immervision-ivp',
'ivu': 'application/vnd.immervision-ivu',
'rif': 'application/reginfo+xml',
'3dml': 'text/vnd.in3d.3dml',
'spot': 'text/vnd.in3d.spot',
'igs': 'model/iges',
'i2g': 'application/vnd.intergeo',
'cdy': 'application/vnd.cinderella',
'xpw': 'application/vnd.intercon.formnet',
'fcs': 'application/vnd.isac.fcs',
'ipfix': 'application/ipfix',
'cer': 'application/pkix-cert',
'pki': 'application/pkixcmp',
'crl': 'application/pkix-crl',
'pkipath': 'application/pkix-pkipath',
'igm': 'application/vnd.insors.igm',
'rcprofile': 'application/vnd.ipunplugged.rcprofile',
'irp': 'application/vnd.irepository.package+xml',
'jad': 'text/vnd.sun.j2me.app-descriptor',
'jar': 'application/java-archive',
'class': 'application/java-vm',
'jnlp': 'application/x-java-jnlp-file',
'ser': 'application/java-serialized-object',
'java': 'text/x-java-source,java',
'js': 'application/javascript',
'json': 'application/json',
'joda': 'application/vnd.joost.joda-archive',
'jpm': 'video/jpm',
'jpeg': 'image/x-citrix-jpeg',
'jpg': 'image/x-citrix-jpeg',
'pjpeg': 'image/pjpeg',
'jpgv': 'video/jpeg',
'ktz': 'application/vnd.kahootz',
'mmd': 'application/vnd.chipnuts.karaoke-mmd',
'karbon': 'application/vnd.kde.karbon',
'chrt': 'application/vnd.kde.kchart',
'kfo': 'application/vnd.kde.kformula',
'flw': 'application/vnd.kde.kivio',
'kon': 'application/vnd.kde.kontour',
'kpr': 'application/vnd.kde.kpresenter',
'ksp': 'application/vnd.kde.kspread',
'kwd': 'application/vnd.kde.kword',
'htke': 'application/vnd.kenameaapp',
'kia': 'application/vnd.kidspiration',
'kne': 'application/vnd.kinar',
'sse': 'application/vnd.kodak-descriptor',
'lasxml': 'application/vnd.las.las+xml',
'latex': 'application/x-latex',
'lbd': 'application/vnd.llamagraphics.life-balance.desktop',
'lbe': 'application/vnd.llamagraphics.life-balance.exchange+xml',
'jam': 'application/vnd.jam',
'apr': 'application/vnd.lotus-approach',
'pre': 'application/vnd.lotus-freelance',
'nsf': 'application/vnd.lotus-notes',
'org': 'application/vnd.lotus-organizer',
'scm': 'application/vnd.lotus-screencam',
'lwp': 'application/vnd.lotus-wordpro',
'lvp': 'audio/vnd.lucent.voice',
'm3u': 'audio/x-mpegurl',
'm4v': 'video/x-m4v',
'hqx': 'application/mac-binhex40',
'portpkg': 'application/vnd.macports.portpkg',
'mgp': 'application/vnd.osgeo.mapguide.package',
'mrc': 'application/marc',
'mrcx': 'application/marcxml+xml',
'mxf': 'application/mxf',
'nbp': 'application/vnd.wolfram.player',
'ma': 'application/mathematica',
'mathml': 'application/mathml+xml',
'mbox': 'application/mbox',
'mc1': 'application/vnd.medcalcdata',
'mscml': 'application/mediaservercontrol+xml',
'cdkey': 'application/vnd.mediastation.cdkey',
'mwf': 'application/vnd.mfer',
'mfm': 'application/vnd.mfmp',
'msh': 'model/mesh',
'mads': 'application/mads+xml',
'mets': 'application/mets+xml',
'mods': 'application/mods+xml',
'meta4': 'application/metalink4+xml',
'mcd': 'application/vnd.mcd',
'flo': 'application/vnd.micrografx.flo',
'igx': 'application/vnd.micrografx.igx',
'es3': 'application/vnd.eszigno3+xml',
'mdb': 'application/x-msaccess',
'asf': 'video/x-ms-asf',
'exe': 'application/x-msdownload',
'cil': 'application/vnd.ms-artgalry',
'cab': 'application/vnd.ms-cab-compressed',
'ims': 'application/vnd.ms-ims',
'application': 'application/x-ms-application',
'clp': 'application/x-msclip',
'mdi': 'image/vnd.ms-modi',
'eot': 'application/vnd.ms-fontobject',
'xls': 'application/vnd.ms-excel',
'xlam': 'application/vnd.ms-excel.addin.macroenabled.12',
'xlsb': 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
'xltm': 'application/vnd.ms-excel.template.macroenabled.12',
'xlsm': 'application/vnd.ms-excel.sheet.macroenabled.12',
'chm': 'application/vnd.ms-htmlhelp',
'crd': 'application/x-mscardfile',
'lrm': 'application/vnd.ms-lrm',
'mvb': 'application/x-msmediaview',
'mny': 'application/x-msmoney',
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx': 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'ppsx': 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'potx': 'application/vnd.openxmlformats-officedocument.presentationml.template',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'obd': 'application/x-msbinder',
'thmx': 'application/vnd.ms-officetheme',
'onetoc': 'application/onenote',
'pya': 'audio/vnd.ms-playready.media.pya',
'pyv': 'video/vnd.ms-playready.media.pyv',
'ppt': 'application/vnd.ms-powerpoint',
'ppam': 'application/vnd.ms-powerpoint.addin.macroenabled.12',
'sldm': 'application/vnd.ms-powerpoint.slide.macroenabled.12',
'pptm': 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
'ppsm': 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
'potm': 'application/vnd.ms-powerpoint.template.macroenabled.12',
'mpp': 'application/vnd.ms-project',
'pub': 'application/x-mspublisher',
'scd': 'application/x-msschedule',
'xap': 'application/x-silverlight-app',
'stl': 'application/vnd.ms-pki.stl',
'cat': 'application/vnd.ms-pki.seccat',
'vsd': 'application/vnd.visio',
'vsdx': 'application/vnd.visio2013',
'wm': 'video/x-ms-wm',
'wma': 'audio/x-ms-wma',
'wax': 'audio/x-ms-wax',
'wmx': 'video/x-ms-wmx',
'wmd': 'application/x-ms-wmd',
'wpl': 'application/vnd.ms-wpl',
'wmz': 'application/x-ms-wmz',
'wmv': 'video/x-ms-wmv',
'wvx': 'video/x-ms-wvx',
'wmf': 'application/x-msmetafile',
'trm': 'application/x-msterminal',
'doc': 'application/msword',
'docm': 'application/vnd.ms-word.document.macroenabled.12',
'dotm': 'application/vnd.ms-word.template.macroenabled.12',
'wri': 'application/x-mswrite',
'wps': 'application/vnd.ms-works',
'xbap': 'application/x-ms-xbap',
'xps': 'application/vnd.ms-xpsdocument',
'mid': 'audio/midi',
'mpy': 'application/vnd.ibm.minipay',
'afp': 'application/vnd.ibm.modcap',
'rms': 'application/vnd.jcp.javame.midlet-rms',
'tmo': 'application/vnd.tmobile-livetv',
'prc': 'application/x-mobipocket-ebook',
'mbk': 'application/vnd.mobius.mbk',
'dis': 'application/vnd.mobius.dis',
'plc': 'application/vnd.mobius.plc',
'mqy': 'application/vnd.mobius.mqy',
'msl': 'application/vnd.mobius.msl',
'txf': 'application/vnd.mobius.txf',
'daf': 'application/vnd.mobius.daf',
'fly': 'text/vnd.fly',
'mpc': 'application/vnd.mophun.certificate',
'mpn': 'application/vnd.mophun.application',
'mj2': 'video/mj2',
'mpga': 'audio/mpeg',
'mxu': 'video/vnd.mpegurl',
'mpeg': 'video/mpeg',
'm21': 'application/mp21',
'mp4a': 'audio/mp4',
'mp4': 'application/mp4',
'm3u8': 'application/vnd.apple.mpegurl',
'mus': 'application/vnd.musician',
'msty': 'application/vnd.muvee.style',
'mxml': 'application/xv+xml',
'ngdat': 'application/vnd.nokia.n-gage.data',
'n-gage': 'application/vnd.nokia.n-gage.symbian.install',
'ncx': 'application/x-dtbncx+xml',
'nc': 'application/x-netcdf',
'nlu': 'application/vnd.neurolanguage.nlu',
'dna': 'application/vnd.dna',
'nnd': 'application/vnd.noblenet-directory',
'nns': 'application/vnd.noblenet-sealer',
'nnw': 'application/vnd.noblenet-web',
'rpst': 'application/vnd.nokia.radio-preset',
'rpss': 'application/vnd.nokia.radio-presets',
'n3': 'text/n3',
'edm': 'application/vnd.novadigm.edm',
'edx': 'application/vnd.novadigm.edx',
'ext': 'application/vnd.novadigm.ext',
'gph': 'application/vnd.flographit',
'ecelp4800': 'audio/vnd.nuera.ecelp4800',
'ecelp7470': 'audio/vnd.nuera.ecelp7470',
'ecelp9600': 'audio/vnd.nuera.ecelp9600',
'oda': 'application/oda',
'ogx': 'application/ogg',
'oga': 'audio/ogg',
'ogv': 'video/ogg',
'dd2': 'application/vnd.oma.dd2+xml',
'oth': 'application/vnd.oasis.opendocument.text-web',
'opf': 'application/oebps-package+xml',
'qbo': 'application/vnd.intu.qbo',
'oxt': 'application/vnd.openofficeorg.extension',
'osf': 'application/vnd.yamaha.openscoreformat',
'weba': 'audio/webm',
'webm': 'video/webm',
'odc': 'application/vnd.oasis.opendocument.chart',
'otc': 'application/vnd.oasis.opendocument.chart-template',
'odb': 'application/vnd.oasis.opendocument.database',
'odf': 'application/vnd.oasis.opendocument.formula',
'odft': 'application/vnd.oasis.opendocument.formula-template',
'odg': 'application/vnd.oasis.opendocument.graphics',
'otg': 'application/vnd.oasis.opendocument.graphics-template',
'odi': 'application/vnd.oasis.opendocument.image',
'oti': 'application/vnd.oasis.opendocument.image-template',
'odp': 'application/vnd.oasis.opendocument.presentation',
'otp': 'application/vnd.oasis.opendocument.presentation-template',
'ods': 'application/vnd.oasis.opendocument.spreadsheet',
'ots': 'application/vnd.oasis.opendocument.spreadsheet-template',
'odt': 'application/vnd.oasis.opendocument.text',
'odm': 'application/vnd.oasis.opendocument.text-master',
'ott': 'application/vnd.oasis.opendocument.text-template',
'ktx': 'image/ktx',
'sxc': 'application/vnd.sun.xml.calc',
'stc': 'application/vnd.sun.xml.calc.template',
'sxd': 'application/vnd.sun.xml.draw',
'std': 'application/vnd.sun.xml.draw.template',
'sxi': 'application/vnd.sun.xml.impress',
'sti': 'application/vnd.sun.xml.impress.template',
'sxm': 'application/vnd.sun.xml.math',
'sxw': 'application/vnd.sun.xml.writer',
'sxg': 'application/vnd.sun.xml.writer.global',
'stw': 'application/vnd.sun.xml.writer.template',
'otf': 'application/x-font-otf',
'osfpvg': 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
'dp': 'application/vnd.osgi.dp',
'pdb': 'application/vnd.palm',
'p': 'text/x-pascal',
'paw': 'application/vnd.pawaafile',
'pclxl': 'application/vnd.hp-pclxl',
'efif': 'application/vnd.picsel',
'pcx': 'image/x-pcx',
'psd': 'image/vnd.adobe.photoshop',
'prf': 'application/pics-rules',
'pic': 'image/x-pict',
'chat': 'application/x-chat',
'p10': 'application/pkcs10',
'p12': 'application/x-pkcs12',
'p7m': 'application/pkcs7-mime',
'p7s': 'application/pkcs7-signature',
'p7r': 'application/x-pkcs7-certreqresp',
'p7b': 'application/x-pkcs7-certificates',
'p8': 'application/pkcs8',
'plf': 'application/vnd.pocketlearn',
'pnm': 'image/x-portable-anymap',
'pbm': 'image/x-portable-bitmap',
'pcf': 'application/x-font-pcf',
'pfr': 'application/font-tdpfr',
'pgn': 'application/x-chess-pgn',
'pgm': 'image/x-portable-graymap',
'png': 'image/x-png',
'ppm': 'image/x-portable-pixmap',
'pskcxml': 'application/pskc+xml',
'pml': 'application/vnd.ctc-posml',
'ai': 'application/postscript',
'pfa': 'application/x-font-type1',
'pbd': 'application/vnd.powerbuilder6',
'pgp': 'application/pgp-signature',
'box': 'application/vnd.previewsystems.box',
'ptid': 'application/vnd.pvi.ptid1',
'pls': 'application/pls+xml',
'str': 'application/vnd.pg.format',
'ei6': 'application/vnd.pg.osasli',
'dsc': 'text/prs.lines.tag',
'psf': 'application/x-font-linux-psf',
'qps': 'application/vnd.publishare-delta-tree',
'wg': 'application/vnd.pmi.widget',
'qxd': 'application/vnd.quark.quarkxpress',
'esf': 'application/vnd.epson.esf',
'msf': 'application/vnd.epson.msf',
'ssf': 'application/vnd.epson.ssf',
'qam': 'application/vnd.epson.quickanime',
'qfx': 'application/vnd.intu.qfx',
'qt': 'video/quicktime',
'rar': 'application/x-rar-compressed',
'ram': 'audio/x-pn-realaudio',
'rmp': 'audio/x-pn-realaudio-plugin',
'rsd': 'application/rsd+xml',
'rm': 'application/vnd.rn-realmedia',
'bed': 'application/vnd.realvnc.bed',
'mxl': 'application/vnd.recordare.musicxml',
'musicxml': 'application/vnd.recordare.musicxml+xml',
'rnc': 'application/relax-ng-compact-syntax',
'rdz': 'application/vnd.data-vision.rdz',
'rdf': 'application/rdf+xml',
'rp9': 'application/vnd.cloanto.rp9',
'jisp': 'application/vnd.jisp',
'rtf': 'application/rtf',
'rtx': 'text/richtext',
'link66': 'application/vnd.route66.link66+xml',
'rss, .xml': 'application/rss+xml',
'shf': 'application/shf+xml',
'st': 'application/vnd.sailingtracker.track',
'svg': 'image/svg+xml',
'sus': 'application/vnd.sus-calendar',
'sru': 'application/sru+xml',
'setpay': 'application/set-payment-initiation',
'setreg': 'application/set-registration-initiation',
'sema': 'application/vnd.sema',
'semd': 'application/vnd.semd',
'semf': 'application/vnd.semf',
'see': 'application/vnd.seemail',
'snf': 'application/x-font-snf',
'spq': 'application/scvp-vp-request',
'spp': 'application/scvp-vp-response',
'scq': 'application/scvp-cv-request',
'scs': 'application/scvp-cv-response',
'sdp': 'application/sdp',
'etx': 'text/x-setext',
'movie': 'video/x-sgi-movie',
'ifm': 'application/vnd.shana.informed.formdata',
'itp': 'application/vnd.shana.informed.formtemplate',
'iif': 'application/vnd.shana.informed.interchange',
'ipk': 'application/vnd.shana.informed.package',
'tfi': 'application/thraud+xml',
'shar': 'application/x-shar',
'rgb': 'image/x-rgb',
'slt': 'application/vnd.epson.salt',
'aso': 'application/vnd.accpac.simply.aso',
'imp': 'application/vnd.accpac.simply.imp',
'twd': 'application/vnd.simtech-mindmapper',
'csp': 'application/vnd.commonspace',
'saf': 'application/vnd.yamaha.smaf-audio',
'mmf': 'application/vnd.smaf',
'spf': 'application/vnd.yamaha.smaf-phrase',
'teacher': 'application/vnd.smart.teacher',
'svd': 'application/vnd.svd',
'rq': 'application/sparql-query',
'srx': 'application/sparql-results+xml',
'gram': 'application/srgs',
'grxml': 'application/srgs+xml',
'ssml': 'application/ssml+xml',
'skp': 'application/vnd.koan',
'sgml': 'text/sgml',
'sdc': 'application/vnd.stardivision.calc',
'sda': 'application/vnd.stardivision.draw',
'sdd': 'application/vnd.stardivision.impress',
'smf': 'application/vnd.stardivision.math',
'sdw': 'application/vnd.stardivision.writer',
'sgl': 'application/vnd.stardivision.writer-global',
'sm': 'application/vnd.stepmania.stepchart',
'sit': 'application/x-stuffit',
'sitx': 'application/x-stuffitx',
'sdkm': 'application/vnd.solent.sdkm+xml',
'xo': 'application/vnd.olpc-sugar',
'au': 'audio/basic',
'wqd': 'application/vnd.wqd',
'sis': 'application/vnd.symbian.install',
'smi': 'application/smil+xml',
'xsm': 'application/vnd.syncml+xml',
'bdm': 'application/vnd.syncml.dm+wbxml',
'xdm': 'application/vnd.syncml.dm+xml',
'sv4cpio': 'application/x-sv4cpio',
'sv4crc': 'application/x-sv4crc',
'sbml': 'application/sbml+xml',
'tsv': 'text/tab-separated-values',
'tiff': 'image/tiff',
'tao': 'application/vnd.tao.intent-module-archive',
'tar': 'application/x-tar',
'tcl': 'application/x-tcl',
'tex': 'application/x-tex',
'tfm': 'application/x-tex-tfm',
'tei': 'application/tei+xml',
'txt': 'text/plain',
'dxp': 'application/vnd.spotfire.dxp',
'sfs': 'application/vnd.spotfire.sfs',
'tsd': 'application/timestamped-data',
'tpt': 'application/vnd.trid.tpt',
'mxs': 'application/vnd.triscape.mxs',
't': 'text/troff',
'tra': 'application/vnd.trueapp',
'ttf': 'application/x-font-ttf',
'ttl': 'text/turtle',
'umj': 'application/vnd.umajin',
'uoml': 'application/vnd.uoml+xml',
'unityweb': 'application/vnd.unity',
'ufd': 'application/vnd.ufdl',
'uri': 'text/uri-list',
'utz': 'application/vnd.uiq.theme',
'ustar': 'application/x-ustar',
'uu': 'text/x-uuencode',
'vcs': 'text/x-vcalendar',
'vcf': 'text/x-vcard',
'vcd': 'application/x-cdlink',
'vsf': 'application/vnd.vsf',
'wrl': 'model/vrml',
'vcx': 'application/vnd.vcx',
'mts': 'model/vnd.mts',
'vtu': 'model/vnd.vtu',
'vis': 'application/vnd.visionary',
'viv': 'video/vnd.vivo',
'ccxml': 'application/ccxml+xml,',
'vxml': 'application/voicexml+xml',
'src': 'application/x-wais-source',
'wbxml': 'application/vnd.wap.wbxml',
'wbmp': 'image/vnd.wap.wbmp',
'wav': 'audio/x-wav',
'davmount': 'application/davmount+xml',
'woff': 'application/x-font-woff',
'wspolicy': 'application/wspolicy+xml',
'webp': 'image/webp',
'wtb': 'application/vnd.webturbo',
'wgt': 'application/widget',
'hlp': 'application/winhlp',
'wml': 'text/vnd.wap.wml',
'wmls': 'text/vnd.wap.wmlscript',
'wmlsc': 'application/vnd.wap.wmlscriptc',
'wpd': 'application/vnd.wordperfect',
'stf': 'application/vnd.wt.stf',
'wsdl': 'application/wsdl+xml',
'xbm': 'image/x-xbitmap',
'xpm': 'image/x-xpixmap',
'xwd': 'image/x-xwindowdump',
'der': 'application/x-x509-ca-cert',
'fig': 'application/x-xfig',
'xhtml': 'application/xhtml+xml',
'xml': 'application/xml',
'xdf': 'application/xcap-diff+xml',
'xenc': 'application/xenc+xml',
'xer': 'application/patch-ops-error+xml',
'rl': 'application/resource-lists+xml',
'rs': 'application/rls-services+xml',
'rld': 'application/resource-lists-diff+xml',
'xslt': 'application/xslt+xml',
'xop': 'application/xop+xml',
'xpi': 'application/x-xpinstall',
'xspf': 'application/xspf+xml',
'xul': 'application/vnd.mozilla.xul+xml',
'xyz': 'chemical/x-xyz',
'yaml': 'text/yaml',
'yang': 'application/yang',
'yin': 'application/yin+xml',
'zir': 'application/vnd.zul',
'zip': 'application/zip',
'zmm': 'application/vnd.handheld-entertainment+xml',
'zaz': 'application/vnd.zzazz.deck+xml',
}
const mimeIcons = {
pdf: 'mdi-pdf-box',
@ -711,4 +20,4 @@ const mimeIcons = {
xlsm: 'mdi-file-excel-outline',
}
export { mimeTypes, mimeIcons }
export { mimeIcons }

3
packages/nc-gui/utils/viewUtils.ts

@ -6,6 +6,7 @@ import MdiFormIcon from '~icons/mdi/form-select'
import MdiCalendarIcon from '~icons/mdi/calendar'
import MdiGalleryIcon from '~icons/mdi/camera-image'
import MdiKanbanIcon from '~icons/mdi/tablet-dashboard'
import MdiMapIcon from '~icons/mdi/map-outline'
import MdiEyeIcon from '~icons/mdi/eye-circle-outline'
import type { Language } from '~/lib'
@ -14,6 +15,7 @@ export const viewIcons: Record<number | string, { icon: any; color: string }> =
[ViewTypes.FORM]: { icon: MdiFormIcon, color: themeV2Colors.pink['500'] },
calendar: { icon: MdiCalendarIcon, color: 'purple' },
[ViewTypes.GALLERY]: { icon: MdiGalleryIcon, color: 'orange' },
[ViewTypes.MAP]: { icon: MdiMapIcon, color: 'blue' },
[ViewTypes.KANBAN]: { icon: MdiKanbanIcon, color: 'green' },
view: { icon: MdiEyeIcon, color: 'blue' },
}
@ -23,6 +25,7 @@ export const viewTypeAlias: Record<number, string> = {
[ViewTypes.FORM]: 'form',
[ViewTypes.GALLERY]: 'gallery',
[ViewTypes.KANBAN]: 'kanban',
[ViewTypes.MAP]: 'map',
}
export const isRtlLang = (lang: keyof typeof Language) => ['fa', 'ar'].includes(lang)

2
packages/nc-lib-gui/package.json

@ -1,6 +1,6 @@
{
"name": "nc-lib-gui",
"version": "0.105.2",
"version": "0.105.3",
"description": "NocoDB GUI",
"author": {
"name": "NocoDB",

75
packages/noco-docs/content/en/setup-and-usages/column-types.md

@ -10,9 +10,9 @@ menuTitle: 'Column Types'
| Type | Description |
|---|---|
| [ID](#id) | Primary column of the table |
<!-- | [ID](#id) | Primary column of the table | -->
| [LinkToAnotherRecord](#linktoanotherrecord) | Has Many or Many To Many columns |
| [ForeignKey](#foreignkey)| Belongs To relation |
<!-- | [ForeignKey](#foreignkey)| Belongs To relation | -->
| [SingleLineText](#singlelinetext) | For short text |
| [LongText](#longtext) | For lengthy string content |
| [Attachment](#attachment) | File attachment column |
@ -26,28 +26,25 @@ menuTitle: 'Column Types'
| [Email](#email) | Email field |
| [URL](#url) | Valid URL field |
| [Number](#number) | Any type of number |
|[Decimal](#decimal)| Fractional number |
|[Currency](#currency)| Currency value |
|[Percent](#percent)| Percentage |
|[Duration](#duration)| Duration |
|[Rating](#rating)| Rating |
|[Formula](#formula)| Formula based generated column |
|[QR Code](#qr-code)| QR Code visualization of another referenced column |
|[Barcode](#barcode)| Barcode visualization of another referenced column |
| [Count](#count) | |
|[DateTime](#datetime)| Date & Time selector |
|[CreateTime](#createtime)| |
|[AutoNumber](#autonumber)| |
|[Geometry](#geometry)| Geometry column |
|[SpecificDBType](#specificdbtype)| Custom DB type option |
| [Decimal](#decimal)| Fractional number |
| [Currency](#currency)| Currency value |
| [Percent](#percent)| Percentage |
| [Duration](#duration)| Duration |
| [Rating](#rating)| Rating |
| [Formula](#formula)| Formula based generated column |
| [Rollup](#rollup)| Performs calculations and aggregations |
| [DateTime](#datetime)| Date & Time selector |
| [QR Code](#qr-code)| QR Code visualization of another referenced column |
| [Barcode](#barcode)| Barcode visualization of another referenced column |
<!-- | [CreateTime](#createtime)| | -->
| [Geometry](#geometry)| Geometry column |
| [GeoData](#geodata)| GeoData column |
| [Json](#json)| Json column |
| [SpecificDBType](#specificdbtype)| Custom DB type option |
## Database Types
### ID
<!-- ### ID
#### Available Database Types
@ -56,20 +53,20 @@ menuTitle: 'Column Types'
|**MySQL**|_All types are available_|int|
|**PostgreSQL**|_All types are available_|int4|
|**SQL Server**|_All types are available_|int|
|**SQLite**|_All types are available_|integer|
|**SQLite**|_All types are available_|integer| -->
### LinkToAnotherRecord
N/A
For more about Link To Another Record, please visit [here](./link-to-another-record).
### ForeignKey
<!-- ### ForeignKey
#### Available Database Types
|Database| Types|
|-----|----------|
|**MySQL**|_All types are available_|
|**PostgreSQL**|_All types are available_|
|**SQL Server**|_All types are available_|
|**SQLite**|_All types are available_|
|**SQLite**|_All types are available_| -->
### SingleLineText
@ -292,16 +289,10 @@ Encodes the value of a reference column as Barcode. Supported barcode formats: C
* Email
Since it's a virtual column, the cell content (Barcode) cannot be changed directly.
### Count
#### Available Database Types
### Rollup
|Database| Types| Default Type|
|-----|----------|----------|
|**MySQL**|int, smallint, mediumint, bigint, serial|int|
|**PostgreSQL**|int, integer, bigint, bigserial, int2, int4, int8, serial, serial2, serial8, smallint, smallserial|int8|
|**SQL Server**|int, bigint, smallint, tinyint|int|
|**SQLite**|int, integer, tinyint, smallint, mediumint, bigint, int2, int8|integer|
For more about rollup, please visit [here](./rollup).
### DateTime
@ -314,7 +305,7 @@ Since it's a virtual column, the cell content (Barcode) cannot be changed direct
|**SQL Server**|datetime, datetime2, datetimeoffset|datetime|
|**SQLite**|datetime, timestamp|datetime|
### CreateTime
<!-- ### CreateTime
#### Available Database Types
@ -323,7 +314,7 @@ Since it's a virtual column, the cell content (Barcode) cannot be changed direct
|**MySQL**|datetime, timestamp, varchar|datetime|
|**PostgreSQL**|timestamp, timestamp without time zone, timestamptz, timestamp with time zone|datetime|
|**SQL Server**|datetime, datetime2, datetimeoffset|datetime|
|**SQLite**|datetime, timestamp|datetime|
|**SQLite**|datetime, timestamp|datetime| -->
### Geometry
@ -337,6 +328,20 @@ Since it's a virtual column, the cell content (Barcode) cannot be changed direct
|**SQL Server**|geometry|
|**SQLite**|text|
<!-- TODO: add GeoData -->
### JSON
#### Available Database Types
|Database| Types|
|-----|----------|
|**MySQL**|json|
|**PostgreSQL**|json|
|**SQL Server**|text, ntext|
|**SQLite**|text|
### SpecificDBType
#### Available Database Types

4
packages/nocodb-sdk/package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "nocodb-sdk",
"version": "0.105.2",
"version": "0.105.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nocodb-sdk",
"version": "0.105.2",
"version": "0.105.3",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",

2
packages/nocodb-sdk/package.json

@ -1,6 +1,6 @@
{
"name": "nocodb-sdk",
"version": "0.105.2",
"version": "0.105.3",
"description": "NocoDB SDK",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",

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

@ -139,7 +139,7 @@ export interface ViewType {
show_system_fields?: boolean;
lock_type?: 'collaborative' | 'locked' | 'personal';
type?: number;
view?: FormType | GridType | GalleryType | KanbanType;
view?: FormType | GridType | GalleryType | KanbanType | MapType;
}
export interface TableInfoType {
@ -398,6 +398,33 @@ export interface KanbanType {
meta?: string | object;
}
export interface GeoLocationType {
/** @format double */
latitude?: number;
/** @format double */
longitude?: number;
}
export interface MapType {
id?: string;
title?: string;
alias?: string;
initial_geo_position?: GeoLocationType;
fk_model_id?: string;
fk_view_id?: string;
fk_geo_data_col_id?: string | null;
columns?: MapColumnType[];
meta?: string | object;
}
export interface MapColumnType {
id?: string;
label?: string;
help?: string;
fk_col_id?: string;
fk_gallery_id?: string;
}
export interface FormType {
id?: string;
title?: string;
@ -502,7 +529,7 @@ export interface HookType {
description?: string;
env?: string;
type?: string;
event?: 'After' | 'Before';
event?: 'after' | 'before';
operation?: 'insert' | 'delete' | 'update';
async?: boolean;
payload?: string;
@ -590,6 +617,7 @@ export interface NormalColumnRequestType {
| 'Collaborator'
| 'Date'
| 'Year'
| 'GeoData'
| 'Time'
| 'PhoneNumber'
| 'Email'
@ -2691,6 +2719,57 @@ export class Api<
format: 'json',
...params,
}),
/**
* No description
*
* @tags DB view
* @name MapCreate
* @request POST:/api/v1/db/meta/tables/{tableId}/maps
* @response `200` `object` OK
*/
mapCreate: (tableId: string, data: MapType, params: RequestParams = {}) =>
this.request<object, any>({
path: `/api/v1/db/meta/tables/${tableId}/maps`,
method: 'POST',
body: data,
type: ContentType.Json,
format: 'json',
...params,
}),
/**
* No description
*
* @tags DB view
* @name MapUpdate
* @request PATCH:/api/v1/db/meta/maps/{mapId}
* @response `200` `void` OK
*/
mapUpdate: (mapId: string, data: MapType, params: RequestParams = {}) =>
this.request<void, any>({
path: `/api/v1/db/meta/maps/${mapId}`,
method: 'PATCH',
body: data,
type: ContentType.Json,
...params,
}),
/**
* No description
*
* @tags DB view
* @name MapRead
* @request GET:/api/v1/db/meta/maps/{mapId}
* @response `200` `MapType` OK
*/
mapRead: (mapId: string, params: RequestParams = {}) =>
this.request<MapType, any>({
path: `/api/v1/db/meta/maps/${mapId}`,
method: 'GET',
format: 'json',
...params,
}),
};
dbViewShare = {
/**

1
packages/nocodb-sdk/src/lib/UITypes.ts

@ -16,6 +16,7 @@ enum UITypes {
Year = 'Year',
Time = 'Time',
PhoneNumber = 'PhoneNumber',
GeoData = 'GeoData',
Email = 'Email',
URL = 'URL',
Number = 'Number',

1
packages/nocodb-sdk/src/lib/globals.ts

@ -3,6 +3,7 @@ export enum ViewTypes {
GALLERY = 2,
GRID = 3,
KANBAN = 4,
MAP = 5,
}
export enum RelationTypes {

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

@ -978,6 +978,9 @@ export class MssqlUi {
case 'Collaborator':
colProp.dt = 'varchar';
break;
case 'GeoData':
colProp.dt = 'varchar';
break;
case 'Date':
colProp.dt = 'date';
@ -1133,6 +1136,9 @@ export class MssqlUi {
case 'Decimal':
return ['decimal', 'float'];
case 'GeoData':
return ['decimal', 'float'];
case 'Currency':
return [
'int',

20
packages/nocodb/package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "nocodb",
"version": "0.105.2",
"version": "0.105.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nocodb",
"version": "0.105.2",
"version": "0.105.3",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@google-cloud/storage": "^5.7.2",
@ -65,7 +65,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-help": "0.2.85",
"nc-lib-gui": "0.105.2",
"nc-lib-gui": "0.105.3",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk",
@ -154,7 +154,7 @@
}
},
"../nocodb-sdk": {
"version": "0.105.2",
"version": "0.105.3",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -11257,9 +11257,9 @@
}
},
"node_modules/nc-lib-gui": {
"version": "0.105.2",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.105.2.tgz",
"integrity": "sha512-uycep2vme3XP6PwvFjJ6wE2fpCNiaJ/ptWR7ZHwRCu500eJGdtonWc00BQf01FitQ1vKq9Cpqqi2b0hoFBLFvQ==",
"version": "0.105.3",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.105.3.tgz",
"integrity": "sha512-w07Y2+nBiUQYiUyURwH9nqvzzxXsz8MALU/MhmWyNe3Z0YhBvPPLLUxtG1WR3USLI925YT0BsAZTn+iSP3ooPw==",
"dependencies": {
"express": "^4.17.1"
}
@ -27875,9 +27875,9 @@
}
},
"nc-lib-gui": {
"version": "0.105.2",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.105.2.tgz",
"integrity": "sha512-uycep2vme3XP6PwvFjJ6wE2fpCNiaJ/ptWR7ZHwRCu500eJGdtonWc00BQf01FitQ1vKq9Cpqqi2b0hoFBLFvQ==",
"version": "0.105.3",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.105.3.tgz",
"integrity": "sha512-w07Y2+nBiUQYiUyURwH9nqvzzxXsz8MALU/MhmWyNe3Z0YhBvPPLLUxtG1WR3USLI925YT0BsAZTn+iSP3ooPw==",
"requires": {
"express": "^4.17.1"
}

4
packages/nocodb/package.json

@ -1,6 +1,6 @@
{
"name": "nocodb",
"version": "0.105.2",
"version": "0.105.3",
"description": "NocoDB Backend",
"main": "dist/bundle.js",
"author": {
@ -105,7 +105,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-help": "0.2.85",
"nc-lib-gui": "0.105.2",
"nc-lib-gui": "0.105.3",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk",

2
packages/nocodb/src/lib/Noco.ts

@ -105,7 +105,7 @@ export default class Noco {
constructor() {
process.env.PORT = process.env.PORT || '8080';
// todo: move
process.env.NC_VERSION = '0104004';
process.env.NC_VERSION = '0105002';
// if env variable NC_MINIMAL_DBS is set, then disable project creation with external sources
if (process.env.NC_MINIMAL_DBS) {

2
packages/nocodb/src/lib/db/sql-client/lib/oracle/OracleClient.ts

@ -1913,7 +1913,7 @@ class OracleClient extends KnexClient {
* @returns {Number} code
* @returns {String} message
*/
async totalRecords(_args: any = {}) {
async totalRecords(_args: any = {}): Promise<Result> {
// @ts-ignore
const func = this.totalRecords.name;
throw new Error('Function not supported for oracle yet');

8
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/helpers/getAst.ts

@ -81,13 +81,13 @@ const getAst = async ({
...(await obj),
[col.title]:
allowedCols && (!includePkByDefault || !col.pk)
? allowedCols[col.id] &&
? (allowedCols[col.id] &&
(!isSystemColumn(col) || view.show_system_fields) &&
(!fields?.length || fields.includes(col.title)) &&
value
: fields?.length
value)
: (fields?.length
? fields.includes(col.title) && value
: value,
: value),
};
}, Promise.resolve({}));
};

19
packages/nocodb/src/lib/db/sql-mgr/SqlMgr.ts

@ -17,6 +17,14 @@ import NcConnectionMgr from '../../utils/common/NcConnectionMgr';
import { customAlphabet } from 'nanoid';
import Debug from '../util/Debug';
import Result from '../util/Result';
import type MssqlClient from '../sql-client/lib/mssql/MssqlClient';
import type MysqlClient from '../sql-client/lib/mysql/MysqlClient';
import type OracleClient from '../sql-client/lib/oracle/OracleClient';
import type PGClient from '../sql-client/lib/pg/PgClient';
import type SnowflakeClient from '../sql-client/lib/snowflake/SnowflakeClient';
import type SqliteClient from '../sql-client/lib/sqlite/SqliteClient';
const randomID = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 20);
const log = new Debug('SqlMgr');
@ -337,7 +345,16 @@ export default class SqlMgr {
* @returns
* @memberof SqlMgr
*/
public async projectGetSqlClient(args) {
public async projectGetSqlClient(
args
): Promise<
| SnowflakeClient
| MysqlClient
| SqliteClient
| MssqlClient
| OracleClient
| PGClient
> {
const func = this.projectGetSqlClient.name;
log.api(`${func}:args:`, args);

2
packages/nocodb/src/lib/db/sql-mgr/v2/SqlMgrv2.ts

@ -118,7 +118,7 @@ export default class SqlMgrv2 {
return sqlMigrationStatements;
}
protected async getSqlClient(base: Base): Promise<any> {
protected async getSqlClient(base: Base) {
return NcConnectionMgrv2.getSqlClient(base);
}
}

2
packages/nocodb/src/lib/db/sql-mgr/v2/SqlMgrv2Trans.ts

@ -52,7 +52,7 @@ export default class SqlMgrv2Trans extends SqlMgrv2 {
}
}
protected async getSqlClient(base: Base): Promise<any> {
protected async getSqlClient(base: Base) {
return NcConnectionMgrv2.getSqlClient(base, this.trx);
}

7
packages/nocodb/src/lib/db/sql-migrator/lib/KnexMigratorv2.ts

@ -1,6 +1,7 @@
import fs from 'fs';
import path from 'path';
import { promisify } from 'util';
import { Knex } from 'knex';
import glob from 'glob';
import SqlClientFactory from '../../sql-client/lib/SqlClientFactory';
@ -428,7 +429,7 @@ export default class KnexMigratorv2 {
// }
}
protected async getSqlClient(base: Base): Promise<any> {
protected async getSqlClient(base: Base) {
return NcConnectionMgrv2.getSqlClient(base);
}
@ -753,7 +754,7 @@ export default class KnexMigratorv2 {
const vm = this;
const trx = sqlClient.knex.isTransaction
? sqlClient.knex
? (sqlClient.knex as Knex.Transaction)
: await sqlClient.knex.transaction();
try {
for (const query of upStatements) {
@ -920,7 +921,7 @@ export default class KnexMigratorv2 {
const vm = this;
const trx = sqlClient.knex.isTransaction
? sqlClient.knex
? (sqlClient.knex as Knex.Transaction)
: await sqlClient.knex.transaction();
try {
for (const query of downStatements) {

18
packages/nocodb/src/lib/db/sql-migrator/lib/KnexMigratorv2Tans.ts

@ -11,6 +11,13 @@ import Noco from '../../../Noco';
import { XKnex } from '../../sql-data-mapper';
import NcMetaIO from '../../../meta/NcMetaIO';
import type MssqlClient from '../../sql-client/lib/mssql/MssqlClient';
import type MysqlClient from '../../sql-client/lib/mysql/MysqlClient';
import type OracleClient from '../../sql-client/lib/oracle/OracleClient';
import type PGClient from '../../sql-client/lib/pg/PgClient';
import type SnowflakeClient from '../../sql-client/lib/snowflake/SnowflakeClient';
import type SqliteClient from '../../sql-client/lib/sqlite/SqliteClient';
export default class KnexMigratorv2Tans extends KnexMigratorv2 {
protected sqlClient: any;
protected ncMeta: NcMetaIO;
@ -24,7 +31,16 @@ export default class KnexMigratorv2Tans extends KnexMigratorv2 {
protected get metaDb(): XKnex {
return this.ncMeta.knex || Noco.ncMeta.knex;
}
protected async getSqlClient(base: Base): Promise<any> {
protected async getSqlClient(
base: Base
): Promise<
| SnowflakeClient
| MysqlClient
| SqliteClient
| MssqlClient
| OracleClient
| PGClient
> {
return this.sqlClient || NcConnectionMgrv2.getSqlClient(base);
}
}

2
packages/nocodb/src/lib/meta/NcMetaMgr.ts

@ -2909,7 +2909,7 @@ export default class NcMetaMgr {
}
}
protected async projectGetSqlClient(args): Promise<any> {
protected async projectGetSqlClient(args) {
const builder = this.getBuilder(args);
return builder?.getSqlClient();
}

79
packages/nocodb/src/lib/meta/api/dataApis/bulkDataAliasApis.ts

@ -1,4 +1,5 @@
import { Request, Response, Router } from 'express';
import { BaseModelSqlv2 } from '../../../db/sql-data-mapper/lib/sql/BaseModelSqlv2';
import Model from '../../../models/Model';
import Base from '../../../models/Base';
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2';
@ -6,70 +7,68 @@ import ncMetaAclMw from '../../helpers/ncMetaAclMw';
import { getViewAndModelFromRequestByAliasOrId } from './helpers';
import apiMetrics from '../../helpers/apiMetrics';
async function bulkDataInsert(req: Request, res: Response) {
type BulkOperation =
| 'bulkInsert'
| 'bulkUpdate'
| 'bulkUpdateAll'
| 'bulkDelete'
| 'bulkDeleteAll';
async function getModelViewBase(req: Request) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
return { model, view, base };
}
async function executeBulkOperation<T extends BulkOperation>(
req: Request,
res: Response,
operation: T,
options: Parameters<typeof BaseModelSqlv2.prototype[T]>
) {
const { model, view, base } = await getModelViewBase(req);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
});
res.json(await baseModel[operation].apply(null, options));
}
res.json(await baseModel.bulkInsert(req.body, { cookie: req }));
async function bulkDataInsert(req: Request, res: Response) {
await executeBulkOperation(req, res, 'bulkInsert', [
req.body,
{ cookie: req },
]);
}
async function bulkDataUpdate(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
});
res.json(await baseModel.bulkUpdate(req.body, { cookie: req }));
await executeBulkOperation(req, res, 'bulkUpdate', [
req.body,
{ cookie: req },
]);
}
// todo: Integrate with filterArrJson bulkDataUpdateAll
async function bulkDataUpdateAll(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
});
res.json(await baseModel.bulkUpdateAll(req.query, req.body, { cookie: req }));
await executeBulkOperation(req, res, 'bulkUpdateAll', [
req.query,
req.body,
{ cookie: req },
]);
}
async function bulkDataDelete(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
});
res.json(await baseModel.bulkDelete(req.body, { cookie: req }));
await executeBulkOperation(req, res, 'bulkDelete', [
req.body,
{ cookie: req },
]);
}
// todo: Integrate with filterArrJson bulkDataDeleteAll
async function bulkDataDeleteAll(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
});
res.json(await baseModel.bulkDeleteAll(req.query));
await executeBulkOperation(req, res, 'bulkDeleteAll', [req.query]);
}
const router = Router({ mergeParams: true });

2
packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts

@ -229,6 +229,8 @@ export async function populateMeta(base: Base, project: Project): Promise<any> {
const columns = (await sqlClient.columnList({ tn: table.table_name }))
?.data?.list;
mapDefaultDisplayValue(columns);
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.table_name,

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

@ -54,6 +54,7 @@ import crypto from 'crypto';
import swaggerApis from './swagger/swaggerApis';
import importApis from './sync/importApis';
import syncSourceApis from './sync/syncSourceApis';
import mapViewApis from './mapViewApis';
const clients: { [id: string]: Socket } = {};
const jobs: { [id: string]: { last_message: any } } = {};
@ -105,6 +106,7 @@ export default function (router: Router, server) {
router.use(swaggerApis);
router.use(syncSourceApis);
router.use(kanbanViewApis);
router.use(mapViewApis);
userApis(router);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save