Browse Source

Merge pull request #6919 from nocodb/nc-fix/misc-ui-fix-2

Nc fix/misc UI fix 2
pull/6921/head
Raju Udava 1 year ago committed by GitHub
parent
commit
3f95f7beb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      packages/nc-gui/components/dashboard/settings/DataSources.vue
  2. 51
      packages/nc-gui/components/dashboard/settings/UIAcl.vue
  3. 35
      packages/nc-gui/components/general/CopyUrl.vue
  4. 15
      packages/nc-gui/components/general/DeleteModal.vue
  5. 15
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  6. 13
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  7. 27
      packages/nc-gui/components/smartsheet/grid/Table.vue
  8. 6
      packages/nc-gui/components/smartsheet/toolbar/ViewInfo.vue
  9. 4
      packages/nc-gui/lang/en.json
  10. 4
      packages/nc-gui/store/config.ts

9
packages/nc-gui/components/dashboard/settings/DataSources.vue

@ -529,7 +529,7 @@ const isEditBaseModalOpen = computed({
</NcTooltip> </NcTooltip>
<NcTooltip> <NcTooltip>
<template #title> <template #title>
{{ $t('general.delete') }} {{ $t('general.remove') }}
</template> </template>
<NcButton <NcButton
v-if="!source.is_meta && !source.is_local" v-if="!source.is_meta && !source.is_local"
@ -581,7 +581,12 @@ const isEditBaseModalOpen = computed({
<LazyDashboardSettingsBaseAudit :source-id="activeBaseId" @close="isBaseAuditModalOpen = false" /> <LazyDashboardSettingsBaseAudit :source-id="activeBaseId" @close="isBaseAuditModalOpen = false" />
</div> </div>
</GeneralModal> </GeneralModal>
<GeneralDeleteModal v-model:visible="isDeleteBaseModalOpen" :entity-name="$t('general.datasource')" :on-delete="deleteBase"> <GeneralDeleteModal
v-model:visible="isDeleteBaseModalOpen"
:entity-name="$t('general.datasource')"
:on-delete="deleteBase"
:delete-label="$t('general.remove')"
>
<template #entity-preview> <template #entity-preview>
<div v-if="toBeDeletedBase" class="flex flex-row items-center py-2 px-3.25 bg-gray-50 rounded-lg text-gray-700 mb-4"> <div v-if="toBeDeletedBase" class="flex flex-row items-center py-2 px-3.25 bg-gray-50 rounded-lg text-gray-700 mb-4">
<GeneralBaseLogo :source-type="toBeDeletedBase.type" /> <GeneralBaseLogo :source-type="toBeDeletedBase.type" />

51
packages/nc-gui/components/dashboard/settings/UIAcl.vue

@ -16,6 +16,8 @@ import {
useNuxtApp, useNuxtApp,
} from '#imports' } from '#imports'
type Role = 'editor' | 'commenter' | 'viewer'
const props = defineProps<{ const props = defineProps<{
sourceId: string sourceId: string
}>() }>()
@ -39,6 +41,12 @@ const tables = ref<any[]>([])
const searchInput = ref('') const searchInput = ref('')
const selectAll = ref({
editor: false,
commenter: false,
viewer: false,
})
const filteredTables = computed(() => const filteredTables = computed(() =>
tables.value.filter( tables.value.filter(
(el) => (el) =>
@ -80,15 +88,21 @@ async function saveUIAcl() {
$e('a:proj-meta:ui-acl') $e('a:proj-meta:ui-acl')
} }
const onRoleCheck = (record: any, role: string) => { const onRoleCheck = (record: any, role: Role) => {
record.disabled[role] = !record.disabled[role] record.disabled[role] = !record.disabled[role]
record.edited = true record.edited = true
selectAll.value[role as Role] = filteredTables.value.every((t) => !t.disabled[role])
} }
onMounted(async () => { onMounted(async () => {
if (tables.value.length === 0) { if (tables.value.length === 0) {
await loadTableList() await loadTableList()
} }
for (const role of roles.value) {
selectAll.value[role as Role] = filteredTables.value.every((t) => !t.disabled[role])
}
}) })
const tableHeaderRenderer = (label: string) => () => h('div', { class: 'text-gray-500' }, label) const tableHeaderRenderer = (label: string) => () => h('div', { class: 'text-gray-500' }, label)
@ -96,11 +110,11 @@ const tableHeaderRenderer = (label: string) => () => h('div', { class: 'text-gra
const columns = [ const columns = [
{ {
title: tableHeaderRenderer(t('labels.tableName')), title: tableHeaderRenderer(t('labels.tableName')),
name: 'table_name', name: 'Table Name',
}, },
{ {
title: tableHeaderRenderer(t('labels.viewName')), title: tableHeaderRenderer(t('labels.viewName')),
name: 'view_name', name: 'View Name',
}, },
{ {
title: tableHeaderRenderer(t('objects.roleType.editor')), title: tableHeaderRenderer(t('objects.roleType.editor')),
@ -118,6 +132,16 @@ const columns = [
width: 120, width: 120,
}, },
] ]
const toggleSelectAll = (role: Role) => {
selectAll.value[role] = !selectAll.value[role]
const enabled = selectAll.value[role]
filteredTables.value.forEach((t) => {
t.disabled[role] = !enabled
t.edited = true
})
}
</script> </script>
<template> <template>
@ -163,12 +187,23 @@ const columns = [
}) })
" "
> >
<template #headerCell="{ column }">
<template v-if="['editor', 'commenter', 'viewer'].includes(column.name)">
<div class="flex flex-row gap-x-1">
<NcCheckbox :checked="selectAll[column.name as Role]" @change="() => toggleSelectAll(column.name)" />
<div class="flex capitalize">
{{ column.name }}
</div>
</div>
</template>
<template v-else>{{ column.name }}</template>
</template>
<template #emptyText> <template #emptyText>
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" /> <a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" />
</template> </template>
<template #bodyCell="{ record, column }"> <template #bodyCell="{ record, column }">
<div v-if="column.name === 'table_name'"> <div v-if="column.name === 'Table Name'">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<div class="min-w-5 flex items-center justify-center"> <div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="{ meta: record.table_meta, type: record.ptype }" class="text-gray-500" /> <GeneralTableIcon :meta="{ meta: record.table_meta, type: record.ptype }" class="text-gray-500" />
@ -179,7 +214,7 @@ const columns = [
</div> </div>
</div> </div>
<div v-if="column.name === 'view_name'"> <div v-if="column.name === 'View Name'">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<div class="min-w-5 flex items-center justify-center"> <div class="min-w-5 flex items-center justify-center">
<GeneralViewIcon :meta="record" class="text-gray-500"></GeneralViewIcon> <GeneralViewIcon :meta="record" class="text-gray-500"></GeneralViewIcon>
@ -202,10 +237,10 @@ const columns = [
> >
</template> </template>
<a-checkbox <NcCheckbox
:checked="!record.disabled[role]" :checked="!record.disabled[role]"
:class="`nc-acl-${record.title}-${role}-chkbox`" :class="`nc-acl-${record.title}-${role}-chkbox !ml-0.25`"
@change="onRoleCheck(record, role)" @change="onRoleCheck(record, role as Role)"
/> />
</a-tooltip> </a-tooltip>
</div> </div>

35
packages/nc-gui/components/general/CopyUrl.vue

@ -40,18 +40,29 @@ const copyUrl = async () => {
<div class="overflow-hidden whitespace-nowrap text-gray-500">{{ url }}</div> <div class="overflow-hidden whitespace-nowrap text-gray-500">{{ url }}</div>
</div> </div>
<div class="flex flex-row gap-x-1"> <div class="flex flex-row gap-x-1">
<div class="button" @click="openUrl"> <NcTooltip>
<RiExternalLinkLine class="h-3.75" /> <template #title>
</div> {{ $t('activity.openInANewTab') }}
<div </template>
class="button"
:class="{ <div class="button" @click="openUrl">
'!text-gray-300 !border-gray-200 !cursor-not-allowed': isCopied.embed, <RiExternalLinkLine class="h-3.75" />
}" </div>
@click="embedHtml" </NcTooltip>
> <NcTooltip>
<MdiCodeTags class="h-4" /> <template #title>
</div> {{ $t('activity.copyIFrameCode') }}
</template>
<div
class="button"
:class="{
'!text-gray-300 !border-gray-200 !cursor-not-allowed': isCopied.embed,
}"
@click="embedHtml"
>
<MdiCodeTags class="h-4" />
</div>
</NcTooltip>
<div class="button" data-testid="docs-share-page-copy-link" @click="copyUrl"> <div class="button" data-testid="docs-share-page-copy-link" @click="copyUrl">
<MdiCheck v-if="isCopied.link" class="h-3.5" /> <MdiCheck v-if="isCopied.link" class="h-3.5" />
<MdiContentCopy v-else class="h-3.5" /> <MdiContentCopy v-else class="h-3.5" />

15
packages/nc-gui/components/general/DeleteModal.vue

@ -5,6 +5,7 @@ const props = defineProps<{
visible: boolean visible: boolean
entityName: string entityName: string
onDelete: () => Promise<void> onDelete: () => Promise<void>
deleteLabel?: string | undefined
}>() }>()
const emits = defineEmits(['update:visible']) const emits = defineEmits(['update:visible'])
@ -12,6 +13,10 @@ const visible = useVModel(props, 'visible', emits)
const isLoading = ref(false) const isLoading = ref(false)
const { t } = useI18n()
const deleteLabel = computed(() => props.deleteLabel ?? t('general.delete'))
const onDelete = async () => { const onDelete = async () => {
isLoading.value = true isLoading.value = true
try { try {
@ -43,11 +48,15 @@ onKeyStroke('Enter', () => {
<GeneralModal v-model:visible="visible" size="small" centered> <GeneralModal v-model:visible="visible" size="small" centered>
<div class="flex flex-col p-6"> <div class="flex flex-col p-6">
<div class="flex flex-row pb-2 mb-4 font-medium text-lg border-b-1 border-gray-50 text-gray-800"> <div class="flex flex-row pb-2 mb-4 font-medium text-lg border-b-1 border-gray-50 text-gray-800">
{{ $t('general.delete') }} {{ props.entityName }} {{ deleteLabel }} {{ props.entityName }}
</div> </div>
<div class="mb-3 text-gray-800"> <div class="mb-3 text-gray-800">
{{ $t('msg.areYouSureUWantTo') }}<span class="ml-1">{{ props.entityName.toLowerCase() }}?</span> {{
$t('msg.areYouSureUWantToDeleteLabel', {
deleteLabel: deleteLabel.toLowerCase(),
})
}}<span class="ml-1">{{ props.entityName.toLowerCase() }}?</span>
</div> </div>
<slot name="entity-preview"></slot> <slot name="entity-preview"></slot>
@ -65,7 +74,7 @@ onKeyStroke('Enter', () => {
data-testid="nc-delete-modal-delete-btn" data-testid="nc-delete-modal-delete-btn"
@click="onDelete" @click="onDelete"
> >
{{ `${$t('general.delete')} ${props.entityName}` }} {{ `${deleteLabel} ${props.entityName}` }}
<template #loading> <template #loading>
{{ $t('general.deleting') }} {{ $t('general.deleting') }}
</template> </template>

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

@ -10,6 +10,8 @@ const props = defineProps<{
const { loadCommentsAndLogs, commentsAndLogs, saveComment: _saveComment, comment, updateComment } = useExpandedFormStoreOrThrow() const { loadCommentsAndLogs, commentsAndLogs, saveComment: _saveComment, comment, updateComment } = useExpandedFormStoreOrThrow()
const { isExpandedFormCommentMode } = storeToRefs(useConfigStore())
const commentsWrapperEl = ref<HTMLDivElement>() const commentsWrapperEl = ref<HTMLDivElement>()
const { user, appInfo } = useGlobal() const { user, appInfo } = useGlobal()
@ -26,6 +28,8 @@ const editLog = ref<AuditType>()
const isEditing = ref<boolean>(false) const isEditing = ref<boolean>(false)
const commentInputDomRef = ref<HTMLInputElement>()
const focusInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus() const focusInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
function onKeyDown(event: KeyboardEvent) { function onKeyDown(event: KeyboardEvent) {
@ -123,6 +127,15 @@ const onClickAudit = () => {
tab.value = 'audits' tab.value = 'audits'
} }
watch(commentInputDomRef, () => {
if (commentInputDomRef.value && isExpandedFormCommentMode.value) {
setTimeout(() => {
commentInputDomRef.value?.focus()
isExpandedFormCommentMode.value = false
}, 400)
}
})
</script> </script>
<template> <template>
@ -240,9 +253,11 @@ const onClickAudit = () => {
<div class="h-14 flex flex-row w-full bg-white py-2.75 px-1.5 items-center rounded-xl border-1 border-gray-200"> <div class="h-14 flex flex-row w-full bg-white py-2.75 px-1.5 items-center rounded-xl border-1 border-gray-200">
<GeneralUserIcon size="base" class="!w-10" :email="user?.email" :name="user?.display_name" /> <GeneralUserIcon size="base" class="!w-10" :email="user?.email" :name="user?.display_name" />
<a-input <a-input
ref="commentInputDomRef"
v-model:value="comment" v-model:value="comment"
class="!rounded-lg border-1 bg-white !px-2.5 !py-2 !border-gray-200 nc-comment-box !outline-none" class="!rounded-lg border-1 bg-white !px-2.5 !py-2 !border-gray-200 nc-comment-box !outline-none"
placeholder="Start typing..." placeholder="Start typing..."
data-testid="expanded-form-comment-input"
:bordered="false" :bordered="false"
@keyup.enter.prevent="saveComment" @keyup.enter.prevent="saveComment"
> >

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

@ -90,6 +90,8 @@ const reloadTrigger = inject(ReloadRowDataHookInj, createEventHook())
const { addOrEditStackRow } = useKanbanViewStoreOrThrow() const { addOrEditStackRow } = useKanbanViewStoreOrThrow()
const { isExpandedFormCommentMode } = storeToRefs(useConfigStore())
// override cell click hook to avoid unexpected behavior at form fields // override cell click hook to avoid unexpected behavior at form fields
provide(CellClickHookInj, undefined) provide(CellClickHookInj, undefined)
@ -283,6 +285,9 @@ const cellWrapperEl = ref()
onMounted(async () => { onMounted(async () => {
isRecordLinkCopied.value = false isRecordLinkCopied.value = false
isLoading.value = true isLoading.value = true
const focusFirstCell = !isExpandedFormCommentMode.value
if (props.loadRow) { if (props.loadRow) {
await _loadRow() await _loadRow()
await loadCommentsAndLogs() await loadCommentsAndLogs()
@ -302,9 +307,11 @@ onMounted(async () => {
isLoading.value = false isLoading.value = false
setTimeout(() => { if (focusFirstCell) {
cellWrapperEl.value?.$el?.querySelector('input,select,textarea')?.focus() setTimeout(() => {
}, 300) cellWrapperEl.value?.$el?.querySelector('input,select,textarea')?.focus()
}, 300)
}
}) })
const addNewRow = () => { const addNewRow = () => {

27
packages/nc-gui/components/smartsheet/grid/Table.vue

@ -144,6 +144,8 @@ const { addUndo, clone, defineViewScope } = useUndoRedo()
const { isViewColumnsLoading, updateGridViewColumn, gridViewCols, resizingColOldWith } = useViewColumnsOrThrow() const { isViewColumnsLoading, updateGridViewColumn, gridViewCols, resizingColOldWith } = useViewColumnsOrThrow()
const { isExpandedFormCommentMode } = storeToRefs(useConfigStore())
const { const {
predictingNextColumn, predictingNextColumn,
predictedNextColumn, predictedNextColumn,
@ -710,6 +712,23 @@ const confirmDeleteRow = (row: number) => {
} }
} }
const commentRow = (rowId: number) => {
try {
isExpandedFormCommentMode.value = true
const row = dataRef.value[rowId]
if (expandForm) {
expandForm(row)
}
activeCell.row = null
activeCell.col = null
selectedRange.clear()
} catch (e: any) {
message.error(e.message)
}
}
const deleteSelectedRangeOfRows = () => { const deleteSelectedRangeOfRows = () => {
deleteRangeOfRows?.(selectedRange).then(() => { deleteRangeOfRows?.(selectedRange).then(() => {
clearSelectedRange() clearSelectedRange()
@ -1712,6 +1731,14 @@ onKeyStroke('ArrowDown', onDown)
{{ $t('general.clear') }} {{ $t('general.clear') }}
</NcMenuItem> </NcMenuItem>
<template v-if="contextMenuTarget && selectedRange.isSingleCell() && isUIAllowed('commentEdit') && !isMobileMode">
<NcDivider />
<NcMenuItem v-e="['a:row:comment']" class="nc-base-menu-item" @click="commentRow(contextMenuTarget.row)">
<MdiMessageOutline class="h-4 w-4" />
{{ $t('general.comment') }}
</NcMenuItem>
</template>
<NcDivider v-if="!(!contextMenuClosing && !contextMenuTarget && data.some((r) => r.rowMeta.selected))" /> <NcDivider v-if="!(!contextMenuClosing && !contextMenuTarget && data.some((r) => r.rowMeta.selected))" />
<NcMenuItem <NcMenuItem
v-if="contextMenuTarget && (selectedRange.isSingleCell() || selectedRange.isSingleRow())" v-if="contextMenuTarget && (selectedRange.isSingleCell() || selectedRange.isSingleRow())"

6
packages/nc-gui/components/smartsheet/toolbar/ViewInfo.vue

@ -43,7 +43,9 @@ const openedBaseUrl = computed(() => {
> >
<NcTooltip class="!text-inherit"> <NcTooltip class="!text-inherit">
<template #title> <template #title>
{{ base?.title }} <span class="capitalize">
{{ base?.title }}
</span>
</template> </template>
<div class="flex flex-row items-center gap-x-1.5"> <div class="flex flex-row items-center gap-x-1.5">
<GeneralProjectIcon <GeneralProjectIcon
@ -59,7 +61,7 @@ const openedBaseUrl = computed(() => {
'!flex': isSharedBase && !isMobileMode, '!flex': isSharedBase && !isMobileMode,
}" }"
> >
<span class="truncate !text-inherit"> <span class="truncate !text-inherit capitalize">
{{ base?.title }} {{ base?.title }}
</span> </span>
</div> </div>

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

@ -69,6 +69,7 @@
"hex": "Hex", "hex": "Hex",
"clear": "Clear", "clear": "Clear",
"slack": "Slack", "slack": "Slack",
"comment": "Comment",
"microsoftTeams": "Microsoft Teams", "microsoftTeams": "Microsoft Teams",
"discord": "Discord", "discord": "Discord",
"matterMost": "Mattermost", "matterMost": "Mattermost",
@ -620,6 +621,8 @@
"newFormLoaded": "New form will be loaded after" "newFormLoaded": "New form will be loaded after"
}, },
"activity": { "activity": {
"openInANewTab": "Open in a new tab",
"copyIFrameCode": "Copy IFrame code",
"onCondition": "On Condition", "onCondition": "On Condition",
"bulkDownload": "Bulk Download", "bulkDownload": "Bulk Download",
"attachFile": "Attach File", "attachFile": "Attach File",
@ -994,6 +997,7 @@
"createWebhookMsg2": "Create web-hooks to power you automations,", "createWebhookMsg2": "Create web-hooks to power you automations,",
"createWebhookMsg3": "Get notified as soon as there are changes in your data", "createWebhookMsg3": "Get notified as soon as there are changes in your data",
"areYouSureUWantTo": "Are you sure you want to delete the following", "areYouSureUWantTo": "Are you sure you want to delete the following",
"areYouSureUWantToDeleteLabel": "Are you sure you want to {deleteLabel} the following",
"idColumnRequired": "ID field is required, you can rename this later if required.", "idColumnRequired": "ID field is required, you can rename this later if required.",
"length59Required": "The length exceeds the max 59 characters", "length59Required": "The length exceeds the max 59 characters",
"noNewNotifications": "You have no new notifications", "noNewNotifications": "You have no new notifications",

4
packages/nc-gui/store/config.ts

@ -13,6 +13,9 @@ export const useConfigStore = defineStore('configStore', () => {
const isViewPortMobile = () => width.value < MAX_WIDTH_FOR_MOBILE_MODE const isViewPortMobile = () => width.value < MAX_WIDTH_FOR_MOBILE_MODE
// When set to true expanded form will auto focus on comment input and state will be set to false after focussing
const isExpandedFormCommentMode = ref(false)
const isMobileMode = ref(isViewPortMobile()) const isMobileMode = ref(isViewPortMobile())
const projectPageTab = ref<'allTable' | 'collaborator' | 'data-source'>('allTable') const projectPageTab = ref<'allTable' | 'collaborator' | 'data-source'>('allTable')
@ -67,6 +70,7 @@ export const useConfigStore = defineStore('configStore', () => {
isViewPortMobile, isViewPortMobile,
handleSidebarOpenOnMobileForNonViews, handleSidebarOpenOnMobileForNonViews,
projectPageTab, projectPageTab,
isExpandedFormCommentMode,
} }
}) })

Loading…
Cancel
Save