Browse Source

Merge pull request #3292 from nocodb/fix/viewer-permission-issues-fixed

vue3: Fixed UI issues for Viewer and Shared view
pull/3298/head
Raju Udava 2 years ago committed by GitHub
parent
commit
8a34bc4b2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      packages/nc-gui-v2/components/shared-view/Grid.vue
  2. 17
      packages/nc-gui-v2/components/smartsheet-toolbar/Export.vue
  3. 80
      packages/nc-gui-v2/components/smartsheet-toolbar/ExportSubActions.vue
  4. 73
      packages/nc-gui-v2/components/smartsheet-toolbar/ViewActions.vue
  5. 19
      packages/nc-gui-v2/components/smartsheet-toolbar/ViewInfo.vue
  6. 2
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  7. 13
      packages/nc-gui-v2/components/smartsheet/Toolbar.vue
  8. 1
      packages/nc-gui-v2/composables/useUIPermission/rolePermissions.ts
  9. 10
      packages/nc-gui-v2/composables/useViewColumns.ts
  10. 6
      packages/nc-gui-v2/layouts/shared-view.vue
  11. 7
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index/index/index.vue

3
packages/nc-gui-v2/components/shared-view/Grid.vue

@ -26,7 +26,8 @@ useProvideSmartsheetStore(sharedView as Ref<TableType>, meta, true, sorts, neste
<style scoped> <style scoped>
.nc-container { .nc-container {
height: calc(100% - var(--header-height)); height: 100%;
padding-bottom: 0.5rem;
flex: 1 1 100%; flex: 1 1 100%;
} }
</style> </style>

17
packages/nc-gui-v2/components/smartsheet-toolbar/Export.vue

@ -0,0 +1,17 @@
<template>
<a-dropdown :trigger="['click']">
<a-button v-t="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn">
<div class="flex gap-2 items-center">
<MdiDownload class="group-hover:text-accent text-gray-500" />
<span class="text-capitalize !text-sm font-weight-normal">Download</span>
<MdiMenuDown class="text-grey" />
</div>
</a-button>
<template #overlay>
<a-menu class="ml-6 !text-sm !px-0 !py-2 !rounded">
<SmartsheetToolbarExportSubActions />
</a-menu>
</template>
</a-dropdown>
</template>

80
packages/nc-gui-v2/components/smartsheet-toolbar/ExportSubActions.vue

@ -0,0 +1,80 @@
<script setup lang="ts">
import { ExportTypes } from 'nocodb-sdk'
import FileSaver from 'file-saver'
import * as XLSX from 'xlsx'
import { message } from 'ant-design-vue'
const isPublicView = inject(IsPublicInj, ref(false))
const fields = inject(FieldsInj, ref([]))
const { project } = useProject()
const { $api } = useNuxtApp()
const meta = inject(MetaInj)
const selectedView = inject(ActiveViewInj)
const exportFile = async (exportType: ExportTypes) => {
let offset = 0
let c = 1
const responseType = exportType === ExportTypes.EXCEL ? 'base64' : 'blob'
try {
while (!isNaN(offset) && offset > -1) {
let res
if (isPublicView.value) {
const { exportFile: sharedViewExportFile } = useSharedView()
res = await sharedViewExportFile(fields.value, offset, exportType, responseType)
} else {
res = await $api.dbViewRow.export(
'noco',
project?.value.title as string,
meta?.value.title as string,
selectedView?.value.title as string,
exportType,
{
responseType,
query: {
offset,
},
} as any,
)
}
const { data, headers } = res
if (exportType === ExportTypes.EXCEL) {
const workbook = XLSX.read(data, { type: 'base64' })
XLSX.writeFile(workbook, `${meta?.value.title}_exported_${c++}.xlsx`)
} else if (exportType === ExportTypes.CSV) {
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
FileSaver.saveAs(blob, `${meta?.value.title}_exported_${c++}.csv`)
}
offset = +headers['nc-export-offset']
if (offset > -1) {
message.info('Downloading more files')
} else {
message.success('Successfully exported all table data')
}
}
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
</script>
<template>
<a-menu-item>
<div v-t="['a:actions:download-csv']" class="nc-project-menu-item" @click="exportFile(ExportTypes.CSV)">
<MdiDownloadOutline class="text-gray-500" />
<!-- Download as CSV -->
{{ $t('activity.downloadCSV') }}
</div>
</a-menu-item>
<a-menu-item>
<div v-t="['a:actions:download-excel']" class="nc-project-menu-item" @click="exportFile(ExportTypes.EXCEL)">
<MdiDownloadOutline class="text-gray-500" />
<!-- Download as XLSX -->
{{ $t('activity.downloadExcel') }}
</div>
</a-menu-item>
</template>

73
packages/nc-gui-v2/components/smartsheet-toolbar/ViewActions.vue

@ -1,21 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import * as XLSX from 'xlsx'
import { ExportTypes } from 'nocodb-sdk'
import FileSaver from 'file-saver'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { LockType } from '~/lib' import { LockType } from '~/lib'
import { viewIcons } from '~/utils' import { viewIcons } from '~/utils'
import { import {
ActiveViewInj, ActiveViewInj,
FieldsInj,
IsLockedInj, IsLockedInj,
IsPublicInj, IsPublicInj,
MetaInj,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
inject, inject,
ref, ref,
useNuxtApp, useNuxtApp,
useProject,
useUIPermission, useUIPermission,
} from '#imports' } from '#imports'
import MdiLockOutlineIcon from '~icons/mdi/lock-outline' import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
@ -28,14 +22,8 @@ const isPublicView = inject(IsPublicInj, ref(false))
const isView = false const isView = false
const { project } = useProject()
const { $api, $e } = useNuxtApp() const { $api, $e } = useNuxtApp()
const meta = inject(MetaInj)
const fields = inject(FieldsInj, ref([]))
const selectedView = inject(ActiveViewInj) const selectedView = inject(ActiveViewInj)
const isLocked = inject(IsLockedInj) const isLocked = inject(IsLockedInj)
@ -46,52 +34,6 @@ const quickImportDialog = ref(false)
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()
const exportFile = async (exportType: ExportTypes) => {
let offset = 0
let c = 1
const responseType = exportType === ExportTypes.EXCEL ? 'base64' : 'blob'
try {
while (!isNaN(offset) && offset > -1) {
let res
if (isPublicView.value) {
const { exportFile: sharedViewExportFile } = useSharedView()
res = await sharedViewExportFile(fields.value, offset, exportType, responseType)
} else {
res = await $api.dbViewRow.export(
'noco',
project?.value.title as string,
meta?.value.title as string,
selectedView?.value.title as string,
exportType,
{
responseType,
query: {
offset,
},
} as any,
)
}
const { data, headers } = res
if (exportType === ExportTypes.EXCEL) {
const workbook = XLSX.read(data, { type: 'base64' })
XLSX.writeFile(workbook, `${meta?.value.title}_exported_${c++}.xlsx`)
} else if (exportType === ExportTypes.CSV) {
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
FileSaver.saveAs(blob, `${meta?.value.title}_exported_${c++}.csv`)
}
offset = +headers['nc-export-offset']
if (offset > -1) {
message.info('Downloading more files')
} else {
message.success('Successfully exported all table data')
}
}
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const Icon = computed(() => { const Icon = computed(() => {
switch ((selectedView?.value as any)?.lock_type) { switch ((selectedView?.value as any)?.lock_type) {
case LockType.Personal: case LockType.Personal:
@ -185,20 +127,7 @@ async function changeLockType(type: LockType) {
</template> </template>
<template #expandIcon></template> <template #expandIcon></template>
<a-menu-item> <SmartsheetToolbarExportSubActions />
<div v-t="['a:actions:download-csv']" class="nc-project-menu-item" @click="exportFile(ExportTypes.CSV)">
<MdiDownloadOutline class="text-gray-500" />
<!-- Download as CSV -->
{{ $t('activity.downloadCSV') }}
</div>
</a-menu-item>
<a-menu-item>
<div v-t="['a:actions:download-excel']" class="nc-project-menu-item" @click="exportFile(ExportTypes.EXCEL)">
<MdiDownloadOutline class="text-gray-500" />
<!-- Download as XLSX -->
{{ $t('activity.downloadExcel') }}
</div>
</a-menu-item>
</a-sub-menu> </a-sub-menu>
<template v-if="isUIAllowed('csvImport') && !isView && !isPublicView"> <template v-if="isUIAllowed('csvImport') && !isView && !isPublicView">
<a-sub-menu key="upload"> <a-sub-menu key="upload">

19
packages/nc-gui-v2/components/smartsheet-toolbar/ViewInfo.vue

@ -0,0 +1,19 @@
<script setup lang="ts">
import { ActiveViewInj, viewIcons } from '#imports'
const selectedView = inject(ActiveViewInj)
</script>
<template>
<div class="flex gap-2 items-center ml-2 mr-2 pr-4 pb-1 py-0.5 border-r-1 border-gray-100">
<component
:is="viewIcons[selectedView?.type].icon"
v-if="selectedView?.type"
class="nc-view-icon group-hover:hidden"
:style="{ color: viewIcons[selectedView?.type].color }"
/>
<span class="!text-sm font-medium max-w-36 overflow-ellipsis overflow-hidden whitespace-nowrap">{{
selectedView?.title
}}</span>
</div>
</template>

2
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -382,7 +382,7 @@ const onNavigate = (dir: NavigateDir) => {
<td key="row-index" class="caption nc-grid-cell pl-5 pr-1"> <td key="row-index" class="caption nc-grid-cell pl-5 pr-1">
<div class="items-center flex gap-1 min-w-[55px]"> <div class="items-center flex gap-1 min-w-[55px]">
<div <div
v-if="!readOnly && !isLocked" v-if="!readOnly || !isLocked"
class="nc-row-no text-xs text-gray-500" class="nc-row-no text-xs text-gray-500"
:class="{ hidden: row.rowMeta.selected }" :class="{ hidden: row.rowMeta.selected }"
> >

13
packages/nc-gui-v2/components/smartsheet/Toolbar.vue

@ -4,7 +4,7 @@ import ToggleDrawer from '~/components/smartsheet/sidebar/toolbar/ToggleDrawer.v
const { isGrid, isForm, isGallery } = useSmartsheetStoreOrThrow() const { isGrid, isForm, isGallery } = useSmartsheetStoreOrThrow()
const isPublic = inject(IsPublicInj, ref(false)) const isPublic = inject(IsPublicInj, ref(false))
const { allowCSVDownload } = useSharedView() const { isUIAllowed } = useUIPermission()
const { isOpen } = useSidebar() const { isOpen } = useSidebar()
</script> </script>
@ -15,11 +15,13 @@ const { isOpen } = useSidebar()
style="z-index: 7" style="z-index: 7"
> >
<SmartsheetToolbarViewActions <SmartsheetToolbarViewActions
v-if="(isGrid && !isPublic) || (isGrid && isPublic && allowCSVDownload)" v-if="isGrid && !isPublic && isUIAllowed('dataInsert')"
:show-system-fields="false" :show-system-fields="false"
class="ml-1" class="ml-1"
/> />
<SmartsheetToolbarViewInfo v-if="!isUIAllowed('dataInsert') && !isPublic" />
<SmartsheetToolbarFieldsMenu v-if="isGrid || isGallery" :show-system-fields="false" class="ml-1" /> <SmartsheetToolbarFieldsMenu v-if="isGrid || isGallery" :show-system-fields="false" class="ml-1" />
<SmartsheetToolbarColumnFilterMenu v-if="isGrid || isGallery" /> <SmartsheetToolbarColumnFilterMenu v-if="isGrid || isGallery" />
@ -28,14 +30,15 @@ const { isOpen } = useSidebar()
<SmartsheetToolbarShareView v-if="(isForm || isGrid) && !isPublic" /> <SmartsheetToolbarShareView v-if="(isForm || isGrid) && !isPublic" />
<SmartsheetToolbarExport v-if="!isUIAllowed('dataInsert')" />
<div class="flex-1" /> <div class="flex-1" />
<SmartsheetToolbarReload class="mx-1" /> <SmartsheetToolbarReload v-if="!isPublic" class="mx-1" />
<SmartsheetToolbarAddRow class="mx-1" /> <SmartsheetToolbarAddRow v-if="isUIAllowed('dataInsert') && !isPublic" class="mx-1" />
<SmartsheetToolbarSearchData v-if="(isGrid || isGallery) && !isPublic" class="shrink mr-2 ml-2" /> <SmartsheetToolbarSearchData v-if="(isGrid || isGallery) && !isPublic" class="shrink mr-2 ml-2" />
<ToggleDrawer v-if="!isOpen" class="mr-2" /> <ToggleDrawer v-if="!isOpen && !isPublic" class="mr-2" />
</div> </div>
</template> </template>

1
packages/nc-gui-v2/composables/useUIPermission/rolePermissions.ts

@ -8,6 +8,7 @@ const rolePermissions = {
column: true, column: true,
tableAttachment: true, tableAttachment: true,
tableRowUpdate: true, tableRowUpdate: true,
dataInsert: true,
rowComments: true, rowComments: true,
gridViewOptions: true, gridViewOptions: true,
sortSync: true, sortSync: true,

10
packages/nc-gui-v2/composables/useViewColumns.ts

@ -19,6 +19,10 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
const { isSharedBase } = useProject() const { isSharedBase } = useProject()
const isLocalMode = computed(
() => isPublic.value || !isUIAllowed('hideAllColumns') || !isUIAllowed('showAllColumns') || isSharedBase.value,
)
const loadViewColumns = async () => { const loadViewColumns = async () => {
if (!meta || !view) return if (!meta || !view) return
@ -52,7 +56,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
} }
const showAll = async (ignoreIds?: any) => { const showAll = async (ignoreIds?: any) => {
if (isPublic.value || isSharedBase.value) { if (isLocalMode.value) {
fields.value = fields.value?.map((field: Field) => ({ fields.value = fields.value?.map((field: Field) => ({
...field, ...field,
show: true, show: true,
@ -75,7 +79,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
reloadData?.() reloadData?.()
} }
const hideAll = async (ignoreIds?: any) => { const hideAll = async (ignoreIds?: any) => {
if (isPublic.value || isSharedBase.value) { if (isLocalMode.value) {
fields.value = fields.value?.map((field: Field) => ({ fields.value = fields.value?.map((field: Field) => ({
...field, ...field,
show: false, show: false,
@ -144,7 +148,7 @@ export function useViewColumns(view: Ref<ViewType> | undefined, meta: ComputedRe
}, },
set(v: boolean) { set(v: boolean) {
if (view?.value?.id) { if (view?.value?.id) {
if (!isPublic.value && !isSharedBase.value) { if (!isLocalMode.value) {
$api.dbView $api.dbView
.update(view.value.id, { .update(view.value.id, {
show_system_fields: v, show_system_fields: v,

6
packages/nc-gui-v2/layouts/shared-view.vue

@ -19,11 +19,11 @@ export default {
</div> </div>
<div> <div>
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<div class="flex items-center gap-2 ml-3"> <div class="flex items-center gap-2 ml-3 text-white">
<template v-if="isLoading"> <template v-if="isLoading">
{{ $t('general.loading') }} <span class="text-white">{{ $t('general.loading') }}</span>
<MdiReload :class="{ 'animate-infinite animate-spin': isLoading }" /> <MdiReload :class="{ 'animate-infinite animate-spin ': isLoading }" />
</template> </template>
<div v-else class="text-xl font-semibold truncate text-white"> <div v-else class="text-xl font-semibold truncate text-white">
{{ sharedView?.title }} {{ sharedView?.title }}

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

@ -12,6 +12,7 @@ const { isOverDropZone } = useDropZone(dropZone, onDrop)
const { files, open, reset } = useFileDialog() const { files, open, reset } = useFileDialog()
const { isSharedBase } = useProject() const { isSharedBase } = useProject()
const { isUIAllowed } = useUIPermission()
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
@ -140,8 +141,12 @@ function onDropZoneClick(e: MouseEvent) {
<template> <template>
<div class="h-full w-full text-gray-600 flex items-center justify-center relative"> <div class="h-full w-full text-gray-600 flex items-center justify-center relative">
<div v-if="isSharedBase" class="flex flex-col gap-6 items-center justify-center mx-auto text-center"> <div
v-if="isSharedBase || !isUIAllowed('dataInsert')"
class="flex flex-col gap-6 items-center justify-center mx-auto text-center text-gray-500 border-gray-300 border-1 w-3/5 h-1/2 rounded-md"
>
<div class="text-3xl">Welcome to NocoDB!</div> <div class="text-3xl">Welcome to NocoDB!</div>
<div class="prose-lg leading-8">To get started, click on a table in the left pane</div>
</div> </div>
<div v-else ref="dropZone"> <div v-else ref="dropZone">
<general-overlay <general-overlay

Loading…
Cancel
Save