Browse Source

Merge pull request #6622 from nocodb/nc-fix/shared-base-editor

Shared base editor fix
pull/6630/head
Muhammed Mustafa 1 year ago committed by GitHub
parent
commit
f2d2d50ed2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      packages/nc-gui/components/cell/Json.vue
  2. 2
      packages/nc-gui/components/dlg/TableDuplicate.vue
  3. 2
      packages/nc-gui/components/dlg/share-and-collaborate/ShareBase.vue
  4. 2
      packages/nc-gui/components/erd/TableNode.vue
  5. 33
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  6. 4
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  7. 4
      packages/nc-gui/components/virtual-cell/components/ListItem.vue
  8. 18
      packages/nc-gui/components/virtual-cell/components/ListItems.vue
  9. BIN
      packages/nc-gui/public/favicon.ico
  10. BIN
      packages/noco-docs/static/img/favicon.ico
  11. BIN
      packages/nocodb/src/public/favicon.ico
  12. 9
      packages/nocodb/src/services/shared-bases.service.ts
  13. 48
      tests/playwright/tests/db/features/baseShare.spec.ts

8
packages/nc-gui/components/cell/Json.vue

@ -42,7 +42,9 @@ const localValueState = ref<string | undefined>()
const error = ref<string | undefined>() const error = ref<string | undefined>()
const isExpanded = inject(JsonExpandInj, ref(false)) const _isExpanded = inject(JsonExpandInj, ref(false))
const isExpanded = ref(false)
const localValue = computed<string | Record<string, any> | undefined>({ const localValue = computed<string | Record<string, any> | undefined>({
get: () => localValueState.value, get: () => localValueState.value,
@ -137,6 +139,10 @@ useSelectedCellKeyupListener(active, (e) => {
break break
} }
}) })
watch(isExpanded, () => {
_isExpanded.value = isExpanded.value
})
</script> </script>
<template> <template>

2
packages/nc-gui/components/dlg/TableDuplicate.vue

@ -80,7 +80,7 @@ const isEaster = ref(false)
<a-divider class="!m-0 !p-0 !my-2" /> <a-divider class="!m-0 !p-0 !my-2" />
<div class="text-xs p-2"> <div class="text-xs p-2">
<a-checkbox v-model:checked="options.includeData">{{ $t('labels.includeData') }}a</a-checkbox> <a-checkbox v-model:checked="options.includeData">{{ $t('labels.includeData') }}</a-checkbox>
<a-checkbox v-model:checked="options.includeViews">{{ $t('labels.includeView') }}</a-checkbox> <a-checkbox v-model:checked="options.includeViews">{{ $t('labels.includeView') }}</a-checkbox>
<a-checkbox v-show="isEaster" v-model:checked="options.includeHooks">{{ $t('labels.includeWebhook') }}</a-checkbox> <a-checkbox v-show="isEaster" v-model:checked="options.includeHooks">{{ $t('labels.includeWebhook') }}</a-checkbox>
</div> </div>

2
packages/nc-gui/components/dlg/share-and-collaborate/ShareBase.vue

@ -159,7 +159,7 @@ const onRoleToggle = async () => {
</div> </div>
<div v-if="isSharedBaseEnabled" class="flex flex-col w-full mt-3 border-t-1 pt-3 border-gray-100"> <div v-if="isSharedBaseEnabled" class="flex flex-col w-full mt-3 border-t-1 pt-3 border-gray-100">
<GeneralCopyUrl v-model:url="url" /> <GeneralCopyUrl v-model:url="url" />
<div class="flex flex-row justify-between mt-3 bg-gray-50 px-3 py-2 rounded-md"> <div v-if="!appInfo.ee" class="flex flex-row justify-between mt-3 bg-gray-50 px-3 py-2 rounded-md">
<div class="text-black">{{ $t('activity.editingAccess') }}</div> <div class="text-black">{{ $t('activity.editingAccess') }}</div>
<a-switch <a-switch
v-e="['c:share:base:role:toggle']" v-e="['c:share:base:role:toggle']"

2
packages/nc-gui/components/erd/TableNode.vue

@ -65,7 +65,7 @@ watch(
:class="[showSkeleton ? '' : '', hasColumns ? '' : '']" :class="[showSkeleton ? '' : '', hasColumns ? '' : '']"
class="text-gray-800 text-sm py-4 border-b-1 border-gray-200 rounded-t-lg w-full h-full px-3 font-medium flex items-center" class="text-gray-800 text-sm py-4 border-b-1 border-gray-200 rounded-t-lg w-full h-full px-3 font-medium flex items-center"
> >
<GeneralTableIcon class="text-primary" :class="{ '!text-6xl !w-auto mr-2': showSkeleton }" :meta="table" /> <GeneralTableIcon class="text-primary" :class="{ '!text-6xl !w-auto mr-2 !h-18': showSkeleton }" :meta="table" />
<div :class="showSkeleton ? 'text-6xl' : ''" class="flex pr-2 pl-1"> <div :class="showSkeleton ? 'text-6xl' : ''" class="flex pr-2 pl-1">
{{ table.title }} {{ table.title }}
</div> </div>

33
packages/nc-gui/components/smartsheet/expanded-form/Comments.vue

@ -8,7 +8,7 @@ const { loadCommentsAndLogs, commentsAndLogs, saveComment: _saveComment, comment
const commentsWrapperEl = ref<HTMLDivElement>() const commentsWrapperEl = ref<HTMLDivElement>()
const { user } = useGlobal() const { user, appInfo } = useGlobal()
const tab = ref<'comments' | 'audits'>('comments') const tab = ref<'comments' | 'audits'>('comments')
@ -98,6 +98,12 @@ const saveComment = async () => {
watch(commentsWrapperEl, () => { watch(commentsWrapperEl, () => {
scrollComments() scrollComments()
}) })
const onClickAudit = () => {
if (appInfo.value.ee) return
tab.value = 'audits'
}
</script> </script>
<template> <template>
@ -108,7 +114,7 @@ watch(commentsWrapperEl, () => {
v-e="['c:row-expand:comment']" v-e="['c:row-expand:comment']"
class="tab flex-1 px-4 py-2 transition-all text-gray-600 cursor-pointer rounded-lg" class="tab flex-1 px-4 py-2 transition-all text-gray-600 cursor-pointer rounded-lg"
:class="{ :class="{
'bg-white shadow !text-brand-500 !hover:text-brand-500': tab === 'comments', 'bg-white shadow !text-brand-500 !hover:text-brand-500': tab === 'comments' || appInfo.ee,
}" }"
@click="tab = 'comments'" @click="tab = 'comments'"
> >
@ -117,13 +123,29 @@ watch(commentsWrapperEl, () => {
Comments Comments
</div> </div>
</div> </div>
<NcTooltip v-if="appInfo.ee" class="tab flex-1">
<template #title>
<span class="!text-base"> Coming soon </span>
</template>
<div
v-e="['c:row-expand:audit']"
class="flex-1 px-4 py-2 transition-all text-gray-400 cursor-not-allowed bg-gray-50 rounded-lg"
@click="onClickAudit"
>
<div class="tab-title nc-tab select-none">
<MdiFileDocumentOutline class="h-4 w-4" />
Audits
</div>
</div>
</NcTooltip>
<div <div
v-else
v-e="['c:row-expand:audit']" v-e="['c:row-expand:audit']"
class="tab flex-1 px-4 py-2 transition-all text-gray-600 cursor-pointer rounded-lg" class="tab flex-1 px-4 py-2 transition-all text-gray-600 cursor-pointer rounded-lg"
:class="{ :class="{
'bg-white shadow !text-brand-500 !hover:text-brand-500': tab === 'audits', 'bg-white shadow !text-brand-500 !hover:text-brand-500': tab === 'audits',
}" }"
@click="tab = 'audits'" @click="onClickAudit"
> >
<div class="tab-title nc-tab"> <div class="tab-title nc-tab">
<MdiFileDocumentOutline class="h-4 w-4" /> <MdiFileDocumentOutline class="h-4 w-4" />
@ -135,7 +157,7 @@ watch(commentsWrapperEl, () => {
<div <div
class="h-[calc(100%-4rem)]" class="h-[calc(100%-4rem)]"
:class="{ :class="{
'pb-2': tab !== 'comments', 'pb-2': tab !== 'comments' && !appInfo.ee,
}" }"
> >
<div v-if="tab === 'comments'" class="flex flex-col h-full"> <div v-if="tab === 'comments'" class="flex flex-col h-full">
@ -251,6 +273,9 @@ watch(commentsWrapperEl, () => {
</template> </template>
<style scoped> <style scoped>
.tab {
@apply max-w-1/2;
}
.tab .tab-title { .tab .tab-title {
@apply min-w-0 flex justify-center gap-2 font-semibold items-center; @apply min-w-0 flex justify-center gap-2 font-semibold items-center;
word-break: 'keep-all'; word-break: 'keep-all';

4
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -41,6 +41,8 @@ const injectedColumn = inject(ColumnInj, ref())
const readonly = inject(ReadonlyInj, ref(false)) const readonly = inject(ReadonlyInj, ref(false))
const { isSharedBase } = storeToRefs(useBase())
const { const {
childrenList, childrenList,
childrenListCount, childrenListCount,
@ -165,6 +167,8 @@ const isDataExist = computed<boolean>(() => {
}) })
const linkOrUnLink = (rowRef: Record<string, string>, id: string) => { const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
if (isSharedBase.value) return
if (isPublic.value && !isForm.value) return if (isPublic.value && !isForm.value) return
if (isNew.value || isChildrenListLinked.value[parseInt(id)]) { if (isNew.value || isChildrenListLinked.value[parseInt(id)]) {
unlinkRow(rowRef, parseInt(id)) unlinkRow(rowRef, parseInt(id))

4
packages/nc-gui/components/virtual-cell/components/ListItem.vue

@ -38,6 +38,8 @@ const row = useVModel(props, 'row')
const isPublic = inject(IsPublicInj, ref(false)) const isPublic = inject(IsPublicInj, ref(false))
const readonly = inject(ReadonlyInj, ref(false))
const { getPossibleAttachmentSrc } = useAttachment() const { getPossibleAttachmentSrc } = useAttachment()
interface Attachment { interface Attachment {
@ -151,7 +153,7 @@ const attachments: ComputedRef<Attachment[]> = computed(() => {
</div> </div>
</div> </div>
<NcButton <NcButton
v-if="!isForm && !isPublic" v-if="!isForm && !isPublic && !readonly"
v-e="['c:row-expand:open']" v-e="['c:row-expand:open']"
type="text" type="text"
size="lg" size="lg"

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

@ -25,6 +25,8 @@ const { isMobileMode } = useGlobal()
const injectedColumn = inject(ColumnInj) const injectedColumn = inject(ColumnInj)
const { isSharedBase } = storeToRefs(useBase())
const filterQueryRef = ref() const filterQueryRef = ref()
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
@ -162,6 +164,15 @@ watch(expandedFormDlg, () => {
onKeyStroke('Escape', () => { onKeyStroke('Escape', () => {
vModel.value = false vModel.value = false
}) })
const onClick = (refRow: any, id: string) => {
if (isSharedBase.value) return
if (isChildrenExcludedListLinked.value[Number.parseInt(id)]) {
unlinkRow(refRow, Number.parseInt(id))
} else {
linkRow(refRow, Number.parseInt(id))
}
}
</script> </script>
<template> <template>
@ -272,12 +283,7 @@ onKeyStroke('Escape', () => {
expandedFormDlg = true expandedFormDlg = true
} }
" "
@click=" @click="() => onClick(refRow, id)"
() => {
if (isChildrenExcludedListLinked[Number.parseInt(id)]) unlinkRow(refRow, Number.parseInt(id))
else linkRow(refRow, Number.parseInt(id))
}
"
/> />
</template> </template>
</div> </div>

BIN
packages/nc-gui/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
packages/noco-docs/static/img/favicon.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
packages/nocodb/src/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

9
packages/nocodb/src/services/shared-bases.service.ts

@ -35,6 +35,10 @@ export class SharedBasesService {
roles = 'viewer'; roles = 'viewer';
} }
if (roles === 'editor' && process.env.NC_CLOUD === 'true') {
NcError.badRequest('Only viewer role is supported');
}
if (!base) { if (!base) {
NcError.badRequest('Invalid base id'); NcError.badRequest('Invalid base id');
} }
@ -80,6 +84,11 @@ export class SharedBasesService {
if (!base) { if (!base) {
NcError.badRequest('Invalid base id'); NcError.badRequest('Invalid base id');
} }
if (roles === 'editor' && process.env.NC_CLOUD === 'true') {
NcError.badRequest('Only viewer role is supported');
}
const data: any = { const data: any = {
uuid: base.uuid || uuidv4(), uuid: base.uuid || uuidv4(),
password: param.password, password: param.password,

48
tests/playwright/tests/db/features/baseShare.spec.ts

@ -67,34 +67,36 @@ test.describe('Shared base', () => {
let url = ''; let url = '';
// share button visible only if a table is opened // share button visible only if a table is opened
await dashboard.treeView.openTable({ title: 'Country' }); await dashboard.treeView.openTable({ title: 'Country' });
url = await dashboard.grid.topbar.getSharedBaseUrl({ role: 'editor', enableSharedBase: true }); if (!isEE()) {
url = await dashboard.grid.topbar.getSharedBaseUrl({ role: 'editor', enableSharedBase: true });
await dashboard.rootPage.waitForTimeout(2000);
// access shared base link await dashboard.rootPage.waitForTimeout(2000);
await dashboard.signOut(); // access shared base link
await dashboard.rootPage.waitForTimeout(2000); await dashboard.signOut();
// todo: Move this to a page object await dashboard.rootPage.waitForTimeout(2000);
await dashboard.rootPage.goto(url); // todo: Move this to a page object
await dashboard.rootPage.goto(url);
await roleTest('editor');
await roleTest('editor');
await loginPage.signIn({
email: `user-${process.env.TEST_PARALLEL_INDEX}@nocodb.com`,
password: getDefaultPwd(),
withoutPrefix: true,
});
await loginPage.signIn({ await dashboard.rootPage.waitForTimeout(1000);
email: `user-${process.env.TEST_PARALLEL_INDEX}@nocodb.com`,
password: getDefaultPwd(),
withoutPrefix: true,
});
await dashboard.rootPage.waitForTimeout(1000); if (isEE()) {
await dashboard.grid.workspaceMenu.switchWorkspace({
workspaceTitle: context.workspace.title,
});
}
if (isEE()) { await dashboard.treeView.openProject({ title: context.base.title, context });
await dashboard.grid.workspaceMenu.switchWorkspace({ await dashboard.treeView.openTable({ title: 'Country' });
workspaceTitle: context.workspace.title,
});
} }
await dashboard.treeView.openProject({ title: context.base.title, context });
await dashboard.treeView.openTable({ title: 'Country' });
url = await dashboard.grid.topbar.getSharedBaseUrl({ role: 'viewer', enableSharedBase: false }); url = await dashboard.grid.topbar.getSharedBaseUrl({ role: 'viewer', enableSharedBase: false });
await dashboard.rootPage.waitForTimeout(2000); await dashboard.rootPage.waitForTimeout(2000);

Loading…
Cancel
Save