diff --git a/packages/nc-gui-v2/components.d.ts b/packages/nc-gui-v2/components.d.ts index 953f50dc03..870ebe047e 100644 --- a/packages/nc-gui-v2/components.d.ts +++ b/packages/nc-gui-v2/components.d.ts @@ -9,6 +9,7 @@ declare module '@vue/runtime-core' { export interface GlobalComponents { AAlert: typeof import('ant-design-vue/es')['Alert'] AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete'] + ABadgeRibbon: typeof import('ant-design-vue/es')['BadgeRibbon'] AButton: typeof import('ant-design-vue/es')['Button'] ACard: typeof import('ant-design-vue/es')['Card'] ACarousel: typeof import('ant-design-vue/es')['Carousel'] @@ -57,6 +58,7 @@ declare module '@vue/runtime-core' { ATableColumn: typeof import('ant-design-vue/es')['TableColumn'] ATabPane: typeof import('ant-design-vue/es')['TabPane'] ATabs: typeof import('ant-design-vue/es')['Tabs'] + ATag: typeof import('ant-design-vue/es')['Tag'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] ATimePicker: typeof import('ant-design-vue/es')['TimePicker'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] @@ -74,9 +76,11 @@ declare module '@vue/runtime-core' { MaterialSymbolsFileCopyOutline: typeof import('~icons/material-symbols/file-copy-outline')['default'] MaterialSymbolsMenu: typeof import('~icons/material-symbols/menu')['default'] MaterialSymbolsTranslate: typeof import('~icons/material-symbols/translate')['default'] + MdiAccountCircle: typeof import('~icons/mdi/account-circle')['default'] MdiAccountGroup: typeof import('~icons/mdi/account-group')['default'] MdiApi: typeof import('~icons/mdi/api')['default'] MdiArrowExpand: typeof import('~icons/mdi/arrow-expand')['default'] + MdiArrowExpandIcon: typeof import('~icons/mdi/arrow-expand-icon')['default'] MdiArrowLeftBold: typeof import('~icons/mdi/arrow-left-bold')['default'] MdiAt: typeof import('~icons/mdi/at')['default'] MdiCalculator: typeof import('~icons/mdi/calculator')['default'] @@ -86,6 +90,7 @@ declare module '@vue/runtime-core' { MdiCheck: typeof import('~icons/mdi/check')['default'] MdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] MdiCloseCircle: typeof import('~icons/mdi/close-circle')['default'] + MdiCloseThick: typeof import('~icons/mdi/close-thick')['default'] MdiContentCopy: typeof import('~icons/mdi/content-copy')['default'] MdiContentSave: typeof import('~icons/mdi/content-save')['default'] MdiDatabase: typeof import('~icons/mdi/database')['default'] @@ -105,6 +110,7 @@ declare module '@vue/runtime-core' { MdiHeart: typeof import('~icons/mdi/heart')['default'] MdiHook: typeof import('~icons/mdi/hook')['default'] MdiInformation: typeof import('~icons/mdi/information')['default'] + MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] MdiLink: typeof import('~icons/mdi/link')['default'] MdiLinkVariantRemove: typeof import('~icons/mdi/link-variant-remove')['default'] MdiLogout: typeof import('~icons/mdi/logout')['default'] @@ -114,6 +120,7 @@ declare module '@vue/runtime-core' { MdiMoonFull: typeof import('~icons/mdi/moon-full')['default'] MdiNotebookCheckOutline: typeof import('~icons/mdi/notebook-check-outline')['default'] MdiOpenInNew: typeof import('~icons/mdi/open-in-new')['default'] + MdiOperator: typeof import('~icons/mdi/operator')['default'] MdiPlus: typeof import('~icons/mdi/plus')['default'] MdiPlusOutline: typeof import('~icons/mdi/plus-outline')['default'] MdiReload: typeof import('~icons/mdi/reload')['default'] @@ -122,6 +129,7 @@ declare module '@vue/runtime-core' { MdiSlack: typeof import('~icons/mdi/slack')['default'] MdiStar: typeof import('~icons/mdi/star')['default'] MdiStore: typeof import('~icons/mdi/store')['default'] + MdiTableArrowRight: typeof import('~icons/mdi/table-arrow-right')['default'] MdiTableBorder: typeof import('~icons/mdi/table-border')['default'] MdiThumbUp: typeof import('~icons/mdi/thumb-up')['default'] MdiTrashCan: typeof import('~icons/mdi/trash-can')['default'] diff --git a/packages/nc-gui-v2/components/cell/MultiSelect.vue b/packages/nc-gui-v2/components/cell/MultiSelect.vue index d33373e547..b8d7f78dc2 100644 --- a/packages/nc-gui-v2/components/cell/MultiSelect.vue +++ b/packages/nc-gui-v2/components/cell/MultiSelect.vue @@ -1,76 +1,196 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + {{ op.title }} + + + + + {{ val }} + + + - + + diff --git a/packages/nc-gui-v2/components/cell/SingleSelect.vue b/packages/nc-gui-v2/components/cell/SingleSelect.vue index 75821474b8..598b1f584e 100644 --- a/packages/nc-gui-v2/components/cell/SingleSelect.vue +++ b/packages/nc-gui-v2/components/cell/SingleSelect.vue @@ -1,71 +1,100 @@ - - - - - - +const options = computed(() => { + if (column?.value.colOptions) { + const opts = column.value.colOptions + ? column.value.colOptions.options.filter((el: SelectOptionType) => el.title !== '') || [] + : [] + for (const op of opts.filter((el: SelectOptionType) => el.order === null)) { + op.title = op.title.replace(/^'/, '').replace(/'$/, '') + } + return opts + } + return [] +}) - + {{ $t('general.cancel') }} + + + + {{ $t('activity.saveRow') }} + + + + + diff --git a/packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue b/packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue new file mode 100644 index 0000000000..fdb11c7f0f --- /dev/null +++ b/packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue b/packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue index 477ee96354..d7e1058a09 100644 --- a/packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue +++ b/packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue @@ -12,9 +12,11 @@ const emits = defineEmits() const { $e } = useNuxtApp() const isView = ref(false) +let showApiSnippet = $ref(false) function onApiSnippet() { // get API snippet + showApiSnippet = true $e('a:view:api-snippet') } @@ -89,6 +91,7 @@ function onOpenModal(type: ViewTypes, title = '') { + +import HTTPSnippet from 'httpsnippet' +import { useClipboard } from '@vueuse/core' +import { notification } from 'ant-design-vue' +import { ActiveViewInj, MetaInj } from '~/context' + +const props = defineProps() + +const emits = defineEmits(['update:modelValue']) + +interface Props { + modelValue: boolean +} + +const { project } = $(useProject()) +const { appInfo, token } = $(useGlobal()) +const meta = $(inject(MetaInj)) +const view = $(inject(ActiveViewInj)) +const { xWhere } = useSmartsheetStoreOrThrow() +const { queryParams } = $(useViewData(meta, view as any, xWhere)) +const { copy } = useClipboard() + +let vModel = $(useVModel(props, 'modelValue', emits)) + +const langs = [ + { + name: 'shell', + clients: ['curl', 'wget'], + }, + { + name: 'javascript', + clients: ['axios', 'fetch', 'jquery', 'xhr'], + }, + { + name: 'node', + clients: ['axios', 'fetch', 'request', 'native', 'unirest'], + }, + { + name: 'nocodb-sdk', + clients: ['javascript', 'node'], + }, + { + name: 'php', + }, + { + name: 'python', + clients: ['python3', 'requests'], + }, + { + name: 'ruby', + }, + { + name: 'java', + }, + { + name: 'c', + }, +] + +const selectedClient = $ref(langs[0].clients && langs[0].clients[0]) + +const selectedLangName = $ref(langs[0].name) + +const apiUrl = $computed( + () => + new URL(`/api/v1/db/data/noco/${project.id}/${meta.title}/views/${view.title}`, (appInfo && appInfo.ncSiteUrl) || '/').href, +) + +const snippet = $computed( + () => + new HTTPSnippet({ + method: 'GET', + headers: [{ name: 'xc-auth', value: token as string, comment: 'JWT Auth token' }], + url: apiUrl, + queryString: Object.entries(queryParams || {}).map(([name, value]) => { + return { + name, + value: String(value), + } + }), + }), +) + +const activeLang = $computed(() => langs.find((lang) => lang.name === selectedLangName)) + +const code = $computed(() => { + if (activeLang?.name === 'nocodb-sdk') { + return `${selectedClient === 'node' ? 'const { Api } require("nocodb-sdk");' : 'import { Api } from "nocodb-sdk";'} +const api = new Api({ + baseURL: ${JSON.stringify(apiUrl)}, + headers: { + "xc-auth": ${JSON.stringify(token as string)} + } +}) + +api.dbViewRow.list( + "noco", + ${JSON.stringify(project.title)}, + ${JSON.stringify(meta.title)}, + ${JSON.stringify(view.title)}, ${JSON.stringify(queryParams, null, 4)}).then(function (data) { + console.log(data); +}).catch(function (error) { + console.error(error); +}); + ` + } + + return snippet.convert(activeLang?.name, selectedClient || (activeLang?.clients && activeLang?.clients[0]), {}) +}) + +const onCopyToClipboard = () => { + copy(code) + notification.info({ message: 'Copied to clipboard' }) +} + +const afterVisibleChange = (visible: boolean) => { + vModel = visible +} + + + + + + Code Snippet + + + + + {{ item.name }} + + + + + Copy to clipboard + + + {{ client }} + + + + + + + 🚀 We are Hiring! 🚀 + + + + + + + + + diff --git a/packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue b/packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue index 330393b5aa..90eab444a0 100644 --- a/packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue +++ b/packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue @@ -3,7 +3,7 @@ import type { ColumnType } from 'nocodb-sdk' import type { Ref } from 'vue' import ItemChip from './components/ItemChip.vue' import ListItems from './components/ListItems.vue' -import { inject, ref, useProvideLTARStore } from '#imports' +import { inject, ref, useProvideLTARStore, useSmartsheetRowStoreOrThrow } from '#imports' import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context' const column = inject(ColumnInj) @@ -18,20 +18,39 @@ const active = false const listItemsDlg = ref(false) +const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow() const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore( column as Ref>, row, + isNew, reloadTrigger.trigger, ) await loadRelatedTableMeta() + +const value = computed(() => { + if (cellValue?.value) { + return cellValue?.value + } else if (isNew.value) { + return state?.value?.[column?.value.title as string] + } + return null +}) + +const unlinkRef = async (rec: Record) => { + if (isNew.value) { + removeLTARRef(rec, column?.value as ColumnType) + } else { + await unlink(rec) + } +} - - + + @@ -40,8 +59,7 @@ await loadRelatedTableMeta() @click="listItemsDlg = true" /> - - + diff --git a/packages/nc-gui-v2/components/virtual-cell/Formula.vue b/packages/nc-gui-v2/components/virtual-cell/Formula.vue index 5265409d61..2efc5928e4 100644 --- a/packages/nc-gui-v2/components/virtual-cell/Formula.vue +++ b/packages/nc-gui-v2/components/virtual-cell/Formula.vue @@ -20,7 +20,7 @@ const showEditFormulaWarningMessage = () => { }, 3000) } -const result = computed(() => (isPg ? handleTZ(value) : value)) +const result = isPg ? handleTZ(value) : value const urls = computed(() => replaceUrlsWithLink(result.value)) diff --git a/packages/nc-gui-v2/components/virtual-cell/HasMany.vue b/packages/nc-gui-v2/components/virtual-cell/HasMany.vue index 38598dcae0..ae69d0f2e9 100644 --- a/packages/nc-gui-v2/components/virtual-cell/HasMany.vue +++ b/packages/nc-gui-v2/components/virtual-cell/HasMany.vue @@ -4,8 +4,8 @@ import type { Ref } from 'vue' import ItemChip from './components/ItemChip.vue' import ListChildItems from './components/ListChildItems.vue' import ListItems from './components/ListItems.vue' -import { computed, inject, ref, useProvideLTARStore } from '#imports' -import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context' +import { computed, inject, ref, useProvideLTARStore, useSmartsheetRowStoreOrThrow } from '#imports' +import { CellValueInj, ColumnInj, IsFormInj, ReloadViewDataHookInj, RowInj } from '~/context' const column = inject(ColumnInj)! @@ -15,20 +15,32 @@ const row = inject(RowInj)! const reloadTrigger = inject(ReloadViewDataHookInj)! +const isForm = inject(IsFormInj) + const listItemsDlg = ref(false) const childListDlg = ref(false) +const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow() const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore( column as Ref>, row, + isNew, reloadTrigger.trigger, ) - await loadRelatedTableMeta() +const localCellValue = computed(() => { + if (cellValue?.value) { + return cellValue?.value + } else if (isNew.value) { + return state?.value?.[column?.value.title as string] + } + return [] +}) + const cells = computed(() => - cellValue.value.reduce((acc: any[], curr: any) => { + localCellValue.value.reduce((acc: any[], curr: any) => { if (!relatedTablePrimaryValueProp.value) return acc const value = curr[relatedTablePrimaryValueProp.value] @@ -38,26 +50,45 @@ const cells = computed(() => return [...acc, { value, item: curr }] }, [] as any[]), ) + +const unlinkRef = async (rec: Record) => { + if (isNew.value) { + removeLTARRef(rec, column?.value as ColumnType) + } else { + await unlink(rec) + } +} - - - - - more... - - - - - - + + + + + more... + + + + + + + + - + { + childListDlg = false + listItemsDlg = true + } + " + /> diff --git a/packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue b/packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue index aa05726fc0..5275a8d3ba 100644 --- a/packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue +++ b/packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue @@ -4,8 +4,8 @@ import type { Ref } from 'vue' import ItemChip from './components/ItemChip.vue' import ListChildItems from './components/ListChildItems.vue' import ListItems from './components/ListItems.vue' -import { computed, inject, ref, useProvideLTARStore } from '#imports' -import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context' +import { computed, inject, ref, useProvideLTARStore, useSmartsheetRowStoreOrThrow } from '#imports' +import { CellValueInj, ColumnInj, IsFormInj, ReloadViewDataHookInj, RowInj } from '~/context' const column = inject(ColumnInj)! @@ -15,20 +15,33 @@ const cellValue = inject(CellValueInj)! const reloadTrigger = inject(ReloadViewDataHookInj)! +const isForm = inject(IsFormInj) + const listItemsDlg = ref(false) const childListDlg = ref(false) +const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow() const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore( column as Ref>, row, + isNew, reloadTrigger.trigger, ) await loadRelatedTableMeta() +const localCellValue = computed(() => { + if (cellValue?.value) { + return cellValue?.value + } else if (isNew.value) { + return state?.value?.[column?.value.title as string] + } + return [] +}) + const cells = computed(() => - cellValue.value.reduce((acc: any[], curr: any) => { + localCellValue.value.reduce((acc: any[], curr: any) => { if (!relatedTablePrimaryValueProp.value) return acc const value = curr[relatedTablePrimaryValueProp.value] @@ -38,27 +51,45 @@ const cells = computed(() => return [...acc, { value, item: curr }] }, [] as any[]), ) + +const unlinkRef = async (rec: Record) => { + if (isNew.value) { + removeLTARRef(rec, column?.value as ColumnType) + } else { + await unlink(rec) + } +} - - - + + + + - more... - - + more... + + - - + + - - + + + - + { + childListDlg = false + listItemsDlg = true + } + " + /> diff --git a/packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue b/packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue index 5364d09b76..e7f0a5a7f7 100644 --- a/packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue +++ b/packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue @@ -1,23 +1,44 @@ - + {{ value }} - - + + + + diff --git a/packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue b/packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue index 7064741c0a..b23650caa3 100644 --- a/packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue +++ b/packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue @@ -1,11 +1,15 @@ - + - + - + - - + Link to '{{ meta.title }}' - + - + { + expandedFormRow = row + expandedFormDlg = true + } + " + > {{ row[relatedTablePrimaryValueProp] @@ -57,14 +87,20 @@ const unlinkRow = async (row: Record) => { - - + + ) => { - + + +