diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss index 3cd427b4b9..25f17ff5e9 100644 --- a/packages/nc-gui/assets/style.scss +++ b/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]; } diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts index 99bc5645f2..2a111be52c 100644 --- a/packages/nc-gui/components.d.ts +++ b/packages/nc-gui/components.d.ts @@ -203,6 +203,8 @@ declare module '@vue/runtime-core' { MdiLogin: typeof import('~icons/mdi/login')['default'] MdiLogout: typeof import('~icons/mdi/logout')['default'] MdiMagnify: typeof import('~icons/mdi/magnify')['default'] + MdiMapMarker: typeof import('~icons/mdi/map-marker')['default'] + MdiMapMarkerAlert: typeof import('~icons/mdi/map-marker-alert')['default'] MdiMenu: typeof import('~icons/mdi/menu')['default'] MdiMenuDown: typeof import('~icons/mdi/menu-down')['default'] MdiMicrosoftTeams: typeof import('~icons/mdi/microsoft-teams')['default'] diff --git a/packages/nc-gui/components/cell/GeoData.vue b/packages/nc-gui/components/cell/GeoData.vue new file mode 100644 index 0000000000..0ab6575361 --- /dev/null +++ b/packages/nc-gui/components/cell/GeoData.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/packages/nc-gui/components/dlg/QuickImport.vue b/packages/nc-gui/components/dlg/QuickImport.vue index 8f3f1bad79..d0f0ad8808 100644 --- a/packages/nc-gui/components/dlg/QuickImport.vue +++ b/packages/nc-gui/components/dlg/QuickImport.vue @@ -31,7 +31,7 @@ interface Props { importDataOnly?: boolean } -const { importType, importDataOnly = false, ...rest } = defineProps() +const { importType, importDataOnly = false, baseId, ...rest } = defineProps() 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 } diff --git a/packages/nc-gui/components/dlg/TableCreate.vue b/packages/nc-gui/components/dlg/TableCreate.vue index 96cb6cec16..c8bec03513 100644 --- a/packages/nc-gui/components/dlg/TableCreate.vue +++ b/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) => message.error(f.errors.join(','))) if (e.errorFields.length) return + } finally { + creating.value = false } - await createTable() } onMounted(() => { @@ -109,7 +114,9 @@ onMounted(() => {
diff --git a/packages/nc-gui/components/dlg/ViewCreate.vue b/packages/nc-gui/components/dlg/ViewCreate.vue index c2db90d5eb..24ef21da74 100644 --- a/packages/nc-gui/components/dlg/ViewCreate.vue +++ b/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() +const { views = [], meta, selectedViewId, groupingFieldColumnId, geoDataFieldColumnId, ...props } = defineProps() const emits = defineEmits() @@ -61,9 +63,10 @@ const form = reactive
({ type: props.type, copy_from_id: null, fk_grp_col_id: null, + fk_geo_data_col_id: null, }) -const singleSelectFieldOptions = ref([]) +const viewSelectFieldOptions = ref([]) 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() { + + +