<script lang="ts" setup>
import { nextTick } from '@vue/runtime-core'
import { message } from 'ant-design-vue'
import { stringifyRolesObj } from 'nocodb-sdk'
import type { BaseType, SourceType, TableType } from 'nocodb-sdk'
import { LoadingOutlined } from '@ant-design/icons-vue'

const indicator = h(LoadingOutlined, {
  class: '!text-gray-400',
  style: {
    fontSize: '0.85rem',
  },
  spin: true,
})

const router = useRouter()

const route = router.currentRoute

const { isSharedBase } = storeToRefs(useBase())

const { setMenuContext, openRenameTableDialog, duplicateTable, contextMenuTarget } = inject(TreeViewInj)!

const base = inject(ProjectInj)!

const basesStore = useBases()

const { isMobileMode } = useGlobal()

const { api } = useApi()

const { auditLogsQuery } = storeToRefs(useWorkspace())

const { createProject: _createProject, updateProject, getProjectMetaInfo, loadProject } = basesStore

const { bases } = storeToRefs(basesStore)

const { loadProjectTables } = useTablesStore()

const { activeTable } = storeToRefs(useTablesStore())

const { appInfo } = useGlobal()

const { orgRoles, isUIAllowed } = useRoles()

useTabs()

const { meta: metaKey, control } = useMagicKeys()

const { refreshCommandPalette } = useCommandPalette()

const editMode = ref(false)

const tempTitle = ref('')

const sourceRenameHelpers = ref<
  Record<
    string,
    {
      editMode: boolean
      tempTitle: string
    }
  >
>({})

const activeBaseId = ref('')

const isErdModalOpen = ref<Boolean>(false)

const { t } = useI18n()

const input = ref<HTMLInputElement>()

const baseRole = computed(() => base.value.project_role || base.value.workspace_role)

const { activeProjectId } = storeToRefs(useBases())

const { baseUrl } = useBase()

const toggleDialog = inject(ToggleDialogInj, () => {})

const { $e } = useNuxtApp()

const isOptionsOpen = ref(false)
const isBasesOptionsOpen = ref<Record<string, boolean>>({})

const activeKey = ref<string[]>([])
const [searchActive] = useToggle()
const filterQuery = ref('')
const keys = ref<Record<string, number>>({})
const isTableDeleteDialogVisible = ref(false)
const isProjectDeleteDialogVisible = ref(false)

const { refreshViewTabTitle } = useViewsStore()

// If only base is open, i.e in case of docs, base view is open and not the page view
const baseViewOpen = computed(() => {
  const routeNameSplit = String(route.value?.name).split('baseId-index-index')
  if (routeNameSplit.length <= 1) return false

  const routeNameAfterProjectView = routeNameSplit[routeNameSplit.length - 1]
  return routeNameAfterProjectView.split('-').length === 2 || routeNameAfterProjectView.split('-').length === 1
})

const showBaseOption = (source: SourceType) => {
  return ['airtableImport', 'csvImport', 'jsonImport', 'excelImport'].some((permission) => isUIAllowed(permission, { source }))
}

const enableEditMode = () => {
  editMode.value = true
  tempTitle.value = base.value.title!
  nextTick(() => {
    input.value?.focus()
    input.value?.select()
    input.value?.scrollIntoView()
  })
}

const enableEditModeForSource = (sourceId: string) => {
  const source = base.value.sources?.find((s) => s.id === sourceId)
  if (!source?.id) return
  sourceRenameHelpers.value[source.id] = {
    editMode: true,
    tempTitle: source.alias || '',
  }
  nextTick(() => {
    const input: HTMLInputElement | null = document.querySelector(`[data-source-rename-input-id="${sourceId}"]`)
    if (!input) return
    input?.focus()
    input?.select()
    input?.scrollIntoView()
  })
}

const updateSourceTitle = async (sourceId: string) => {
  const source = base.value.sources?.find((s) => s.id === sourceId)

  if (!source?.id || !sourceRenameHelpers.value[source.id]) return

  if (sourceRenameHelpers.value[source.id].tempTitle) {
    sourceRenameHelpers.value[source.id].tempTitle = sourceRenameHelpers.value[source.id].tempTitle.trim()
  }

  if (!sourceRenameHelpers.value[source.id].tempTitle) return

  try {
    await api.source.update(source.base_id, source.id, {
      alias: sourceRenameHelpers.value[source.id].tempTitle,
    })

    await loadProject(source.base_id, true)

    delete sourceRenameHelpers.value[source.id]

    $e('a:source:rename')

    refreshViewTabTitle?.()
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  } finally {
    refreshCommandPalette()
  }
}

const updateProjectTitle = async () => {
  if (tempTitle.value) {
    tempTitle.value = tempTitle.value.trim()
  }

  if (!tempTitle.value) return

  try {
    await updateProject(base.value.id!, {
      title: tempTitle.value,
    })
    editMode.value = false
    tempTitle.value = ''

    $e('a:base:rename')

    refreshViewTabTitle?.()
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  }
}

const { copy } = useCopy(true)

const copyProjectInfo = async () => {
  try {
    if (
      await copy(
        Object.entries(await getProjectMetaInfo(base.value.id!)!)
          .map(([k, v]) => `${k}: **${v}**`)
          .join('\n'),
      )
    ) {
      // Copied to clipboard
      message.info(t('msg.info.copiedToClipboard'))
    }
  } catch (e: any) {
    console.error(e)
    message.error(e.message)
  }
}

defineExpose({
  enableEditMode,
})

const setColor = async (color: string, base: BaseType) => {
  try {
    const meta = {
      ...parseProp(base.meta),
      iconColor: color,
    }

    basesStore.updateProject(base.id!, { meta: JSON.stringify(meta) })

    $e('a:base:icon:color:navdraw', { iconColor: color })
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  } finally {
    refreshCommandPalette()
  }
}

function openTableCreateDialog(sourceIndex?: number | undefined) {
  const isOpen = ref(true)
  let sourceId = base.value!.sources?.[0].id
  if (typeof sourceIndex === 'number') {
    sourceId = base.value!.sources?.[sourceIndex].id
  }

  if (!sourceId || !base.value?.id) return

  const { close } = useDialog(resolveComponent('DlgTableCreate'), {
    'modelValue': isOpen,
    sourceId, // || sources.value[0].id,
    'baseId': base.value!.id,
    'onCreate': closeDialog,
    'onUpdate:modelValue': () => closeDialog(),
  })

  function closeDialog(table?: TableType) {
    isOpen.value = false

    if (!table) return

    base.value.isExpanded = true

    if (!activeKey.value || !activeKey.value.includes(`collapse-${sourceId}`)) {
      activeKey.value.push(`collapse-${sourceId}`)
    }

    // TODO: Better way to know when the table node dom is available
    setTimeout(() => {
      const newTableDom = document.querySelector(`[data-table-id="${table.id}"]`)
      if (!newTableDom) return

      newTableDom?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
    }, 1000)

    close(1000)
  }
}

const isAddNewProjectChildEntityLoading = ref(false)

async function addNewProjectChildEntity() {
  if (isAddNewProjectChildEntityLoading.value) return

  isAddNewProjectChildEntityLoading.value = true

  const isProjectPopulated = basesStore.isProjectPopulated(base.value.id!)
  if (!isProjectPopulated && base.value.type === NcProjectType.DB) {
    // We do not wait for tables api, so that add new table is seamless.
    // Only con would be while saving table duplicate table name FE validation might not work
    // If the table list api takes time to load before the table name validation
    loadProjectTables(base.value.id!)
  }

  try {
    openTableCreateDialog()

    if (!base.value.isExpanded && base.value.type !== NcProjectType.DB) {
      base.value.isExpanded = true
    }
  } finally {
    isAddNewProjectChildEntityLoading.value = false
  }
}

const onProjectClick = async (base: NcProject, ignoreNavigation?: boolean, toggleIsExpanded?: boolean) => {
  if (!base) {
    return
  }
  const cmdOrCtrl = isMac() ? metaKey.value : control.value

  if (!toggleIsExpanded && !cmdOrCtrl) $e('c:base:open')

  ignoreNavigation = isMobileMode.value || ignoreNavigation
  toggleIsExpanded = isMobileMode.value || toggleIsExpanded

  if (cmdOrCtrl && !ignoreNavigation) {
    await navigateTo(
      `${cmdOrCtrl ? '#' : ''}${baseUrl({
        id: base.id!,
        type: 'database',
        isSharedBase: isSharedBase.value,
      })}`,
      cmdOrCtrl
        ? {
            open: navigateToBlankTargetOpenOption,
          }
        : undefined,
    )
    return
  }

  if (toggleIsExpanded) {
    base.isExpanded = !base.isExpanded
  } else {
    base.isExpanded = true
  }

  const isProjectPopulated = basesStore.isProjectPopulated(base.id!)

  if (!isProjectPopulated) base.isLoading = true

  if (!ignoreNavigation) {
    await navigateTo(
      baseUrl({
        id: base.id!,
        type: 'database',
        isSharedBase: isSharedBase.value,
      }),
    )
  }

  if (!isProjectPopulated) {
    await loadProjectTables(base.id!)
  }

  if (!isProjectPopulated) {
    base.isLoading = false

    const updatedProject = bases.value.get(base.id!)!
    updatedProject.isLoading = false
  }
}

function openErdView(source: SourceType) {
  $e('c:project:relation')

  const isOpen = ref(true)

  const { close } = useDialog(resolveComponent('DlgProjectErd'), {
    'modelValue': isOpen,
    'sourceId': source!.id,
    'onUpdate:modelValue': () => closeDialog(),
    'baseId': base.value.id,
  })

  function closeDialog() {
    isOpen.value = false

    close(1000)
  }
}

const contextMenuBase = computed(() => {
  if (contextMenuTarget.type === 'source') {
    return contextMenuTarget.value
  } else if (contextMenuTarget.type === 'table') {
    const source = base.value?.sources?.find((b) => b.id === contextMenuTarget.value.source_id)
    if (source) return source
  }
  return null
})

watch(
  () => activeTable.value?.id,
  async () => {
    if (!activeTable.value) return

    const sourceId = activeTable.value.source_id
    if (!sourceId) return

    if (!activeKey.value.includes(`collapse-${sourceId}`)) {
      activeKey.value.push(`collapse-${sourceId}`)
    }
  },
  {
    immediate: true,
  },
)

onKeyStroke('Escape', () => {
  if (isOptionsOpen.value) {
    isOptionsOpen.value = false
  }

  for (const key of Object.keys(isBasesOptionsOpen.value)) {
    isBasesOptionsOpen.value[key] = false
  }
})

const isDuplicateDlgOpen = ref(false)
const selectedProjectToDuplicate = ref()

const duplicateProject = (base: BaseType) => {
  selectedProjectToDuplicate.value = base
  isDuplicateDlgOpen.value = true
}

const tableDelete = () => {
  isTableDeleteDialogVisible.value = true
  $e('c:table:delete')
}

const projectDelete = () => {
  isProjectDeleteDialogVisible.value = true
  $e('c:project:delete')
}

// Tracks if the table ID has been successfully copied to the clipboard
const isTableIdCopied = ref(false)

let tableIdCopiedTimeout: NodeJS.Timeout

const onTableIdCopy = async () => {
  if (tableIdCopiedTimeout) {
    clearTimeout(tableIdCopiedTimeout)
  }

  try {
    await copy(contextMenuTarget.value.id)
    isTableIdCopied.value = true

    tableIdCopiedTimeout = setTimeout(() => {
      isTableIdCopied.value = false
      clearTimeout(tableIdCopiedTimeout)
    }, 5000)
  } catch (e: any) {
    message.error(e.message)
  }
}

const getSource = (sourceId: string) => {
  return base.value.sources?.find((s) => s.id === sourceId)
}

async function openAudit(source: SourceType) {
  $e('c:project:audit')

  auditLogsQuery.value = {
    ...auditLogsQuery.value,
    user: undefined,
    dateRange: undefined,
    dateRangeLabel: undefined,
    startDate: undefined,
    endData: undefined,
    orderBy: {
      created_at: 'desc',
      user: undefined,
    },
  }

  const isOpen = ref(true)

  const { close } = useDialog(resolveComponent('DlgProjectAudit'), {
    'modelValue': isOpen,
    'sourceId': source!.id,
    'onUpdate:modelValue': () => closeDialog(),
    'baseId': base.value!.id,
    'bordered': true,
  })

  function closeDialog() {
    isOpen.value = false

    close(1000)
  }
}
</script>

<template>
  <NcDropdown :trigger="['contextmenu']" overlay-class-name="nc-dropdown-tree-view-context-menu">
    <div
      class="mx-1 nc-base-sub-menu rounded-md"
      :class="{ active: base.isExpanded }"
      :data-testid="`nc-sidebar-base-${base.title}`"
      :data-base-id="base.id"
    >
      <div class="flex items-center gap-0.75 py-0.5 cursor-pointer" @contextmenu="setMenuContext('base', base)">
        <div
          ref="baseNodeRefs"
          :class="{
            'bg-primary-selected active': activeProjectId === base.id && baseViewOpen && !isMobileMode,
            'hover:bg-gray-200': !(activeProjectId === base.id && baseViewOpen),
          }"
          :data-testid="`nc-sidebar-base-title-${base.title}`"
          class="nc-sidebar-node base-title-node h-7 flex-grow rounded-md group flex items-center w-full pr-1 pl-1.5"
        >
          <div class="flex items-center mr-1" @click="onProjectClick(base)">
            <div class="flex items-center select-none w-6 h-full">
              <a-spin v-if="base.isLoading" class="!ml-1.25 !flex !flex-row !items-center !my-0.5 w-8" :indicator="indicator" />

              <div v-else>
                <GeneralBaseIconColorPicker
                  :key="`${base.id}_${parseProp(base.meta).iconColor}`"
                  :type="base?.type"
                  :model-value="parseProp(base.meta).iconColor"
                  size="small"
                  :readonly="(base?.type && base?.type !== 'database') || !isUIAllowed('baseRename')"
                  @update:model-value="setColor($event, base)"
                >
                </GeneralBaseIconColorPicker>
              </div>
            </div>
          </div>

          <input
            v-if="editMode"
            ref="input"
            v-model="tempTitle"
            class="flex-grow leading-1 outline-0 ring-none capitalize !text-inherit !bg-transparent flex-1 mr-4"
            :class="activeProjectId === base.id && baseViewOpen ? '!text-brand-600 !font-semibold' : '!text-gray-700'"
            @click.stop
            @keyup.enter="updateProjectTitle"
            @keyup.esc="updateProjectTitle"
            @blur="updateProjectTitle"
          />
          <NcTooltip
            v-else
            class="nc-sidebar-node-title capitalize text-ellipsis overflow-hidden select-none flex-1"
            :style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
            :class="activeProjectId === base.id && baseViewOpen ? 'text-brand-600 font-semibold' : 'text-gray-700'"
            show-on-truncate-only
            @click="onProjectClick(base)"
          >
            <template #title>{{ base.title }}</template>
            <span>
              {{ base.title }}
            </span>
          </NcTooltip>

          <template v-if="!editMode">
            <NcDropdown v-if="!isSharedBase" v-model:visible="isOptionsOpen" :trigger="['click']">
              <NcButton
                v-e="['c:base:options']"
                class="nc-sidebar-node-btn"
                :class="{ '!text-black !opacity-100 !inline-block': isOptionsOpen }"
                data-testid="nc-sidebar-context-menu"
                type="text"
                size="xxsmall"
                @click.stop
              >
                <GeneralIcon icon="threeDotHorizontal" class="text-xl w-4.75" />
              </NcButton>
              <template #overlay>
                <NcMenu
                  class="nc-scrollbar-md"
                  :style="{
                    maxHeight: '70vh',
                    overflow: 'overlay',
                  }"
                  :data-testid="`nc-sidebar-base-${base.title}-options`"
                  @click="isOptionsOpen = false"
                >
                  <template v-if="!isSharedBase">
                    <NcMenuItem v-if="isUIAllowed('baseRename')" data-testid="nc-sidebar-project-rename" @click="enableEditMode">
                      <div v-e="['c:base:rename']" class="flex gap-2 items-center">
                        <GeneralIcon icon="rename" class="group-hover:text-black" />
                        {{ $t('general.rename') }}
                      </div>
                    </NcMenuItem>

                    <NcMenuItem
                      v-if="isUIAllowed('baseDuplicate', { roles: [stringifyRolesObj(orgRoles), baseRole].join() })"
                      data-testid="nc-sidebar-base-duplicate"
                      @click="duplicateProject(base)"
                    >
                      <div v-e="['c:base:duplicate']" class="flex gap-2 items-center">
                        <GeneralIcon icon="duplicate" class="text-gray-700" />
                        {{ $t('general.duplicate') }}
                      </div>
                    </NcMenuItem>

                    <NcDivider v-if="['baseDuplicate', 'baseRename'].some((permission) => isUIAllowed(permission))" />

                    <!-- Copy Project Info -->
                    <NcMenuItem
                      v-if="!isEeUI"
                      key="copy"
                      data-testid="nc-sidebar-base-copy-base-info"
                      @click.stop="copyProjectInfo"
                    >
                      <div v-e="['c:base:copy-proj-info']" class="flex gap-2 items-center">
                        <GeneralIcon icon="copy" class="group-hover:text-black" />
                        {{ $t('activity.account.projInfo') }}
                      </div>
                    </NcMenuItem>

                    <!-- ERD View -->
                    <NcMenuItem
                      v-if="base?.sources?.[0]?.enabled"
                      key="erd"
                      data-testid="nc-sidebar-base-relations"
                      @click="openErdView(base?.sources?.[0])"
                    >
                      <div v-e="['c:base:erd']" class="flex gap-2 items-center">
                        <GeneralIcon icon="erd" />
                        {{ $t('title.relations') }}
                      </div>
                    </NcMenuItem>

                    <!-- Audit -->
                    <NcMenuItem
                      v-if="isUIAllowed('baseAuditList') && base?.sources?.[0]?.enabled"
                      key="audit"
                      data-testid="nc-sidebar-base-audit"
                      @click="openAudit(base?.sources?.[0])"
                    >
                      <GeneralIcon icon="audit" class="group-hover:text-black" />
                      {{ $t('title.audit') }}
                    </NcMenuItem>

                    <!-- Swagger: Rest APIs -->
                    <NcMenuItem
                      v-if="isUIAllowed('apiDocs')"
                      key="api"
                      data-testid="nc-sidebar-base-rest-apis"
                      @click.stop="
                        () => {
                          $e('c:base:api-docs')
                          openLink(`/api/v2/meta/bases/${base.id}/swagger`, appInfo.ncSiteUrl)
                        }
                      "
                    >
                      <div v-e="['c:base:api-docs']" class="flex gap-2 items-center">
                        <GeneralIcon icon="snippet" class="group-hover:text-black !max-w-3.9" />
                        {{ $t('activity.account.swagger') }}
                      </div>
                    </NcMenuItem>
                  </template>

                  <template v-if="base?.sources?.[0]?.enabled && showBaseOption(base?.sources?.[0])">
                    <NcDivider />
                    <DashboardTreeViewBaseOptions v-model:base="base" :source="base.sources[0]" />
                  </template>

                  <NcDivider v-if="['baseMiscSettings', 'baseDelete'].some((permission) => isUIAllowed(permission))" />

                  <NcMenuItem
                    v-if="isUIAllowed('baseMiscSettings')"
                    key="teamAndSettings"
                    data-testid="nc-sidebar-base-settings"
                    class="nc-sidebar-base-base-settings"
                    @click="toggleDialog(true, 'teamAndAuth', undefined, base.id)"
                  >
                    <div v-e="['c:base:settings']" class="flex gap-2 items-center">
                      <GeneralIcon icon="settings" class="group-hover:text-black" />
                      {{ $t('activity.settings') }}
                    </div>
                  </NcMenuItem>
                  <NcMenuItem
                    v-if="isUIAllowed('baseDelete', { roles: [stringifyRolesObj(orgRoles), baseRole].join() })"
                    data-testid="nc-sidebar-base-delete"
                    class="!text-red-500 !hover:bg-red-50"
                    @click="projectDelete"
                  >
                    <div class="flex gap-2 items-center">
                      <GeneralIcon icon="delete" class="w-4" />
                      {{ $t('general.delete') }}
                    </div>
                  </NcMenuItem>
                </NcMenu>
              </template>
            </NcDropdown>

            <NcButton
              v-if="isUIAllowed('tableCreate', { roles: baseRole, source: base?.sources?.[0] })"
              v-e="['c:base:create-table']"
              :disabled="!base?.sources?.[0]?.enabled"
              class="nc-sidebar-node-btn"
              size="xxsmall"
              type="text"
              data-testid="nc-sidebar-add-base-entity"
              :class="{
                '!text-black !inline-block !opacity-100': isAddNewProjectChildEntityLoading,
                '!inline-block !opacity-100': isOptionsOpen,
              }"
              :loading="isAddNewProjectChildEntityLoading"
              @click.stop="addNewProjectChildEntity"
            >
              <GeneralIcon icon="plus" class="text-xl leading-5" style="-webkit-text-stroke: 0.15px" />
            </NcButton>

            <NcButton
              v-e="['c:base:expand']"
              type="text"
              size="xxsmall"
              class="nc-sidebar-node-btn nc-sidebar-expand !xs:opacity-100 !mr-0 mt-0.5"
              :class="{
                '!opacity-100': isOptionsOpen,
              }"
              @click="onProjectClick(base, true, true)"
            >
              <GeneralIcon
                icon="chevronRight"
                class="group-hover:visible cursor-pointer transform transition-transform duration-200 text-[20px]"
                :class="{ '!rotate-90': base.isExpanded }"
              />
            </NcButton>
          </template>
        </div>
      </div>

      <div
        v-if="base.id && !base.isLoading"
        key="g1"
        class="overflow-x-hidden transition-max-height"
        :class="{ 'max-h-0': !base.isExpanded }"
      >
        <template v-if="base && base?.sources">
          <div class="flex-1 overflow-y-auto overflow-x-hidden flex flex-col" :class="{ 'mb-[20px]': isSharedBase }">
            <div v-if="base?.sources?.[0]?.enabled" class="flex-1">
              <div class="transition-height duration-200">
                <DashboardTreeViewTableList :base="base" :source-index="0" />
              </div>
            </div>

            <div v-if="base?.sources?.slice(1).filter((el) => el.enabled)?.length" class="transition-height duration-200">
              <div class="border-none sortable-list">
                <div v-for="(source, sourceIndex) of base.sources" :key="`source-${source.id}`">
                  <template v-if="sourceIndex === 0"></template>
                  <a-collapse
                    v-else-if="source && source.enabled"
                    v-model:activeKey="activeKey"
                    v-e="['c:source:toggle-expand']"
                    class="!mx-0 !px-0 nc-sidebar-source-node"
                    :class="[{ hidden: searchActive && !!filterQuery }]"
                    expand-icon-position="left"
                    :bordered="false"
                    ghost
                  >
                    <template #expandIcon="{ isActive }">
                      <NcButton
                        v-e="['c:external:base:expand']"
                        type="text"
                        size="xxsmall"
                        class="nc-sidebar-node-btn nc-sidebar-expand !xs:opacity-100 !mr-0 mt-0.5"
                        :class="{ '!opacity-100 !inline-block': isBasesOptionsOpen[source!.id!] }"
                      >
                        <GeneralIcon
                          icon="chevronDown"
                          class="flex-none cursor-pointer transform transition-transform duration-500 rotate-270"
                          :class="{ '!rotate-180': isActive }"
                        />
                      </NcButton>
                    </template>
                    <a-collapse-panel :key="`collapse-${source.id}`">
                      <template #header>
                        <div class="nc-sidebar-node min-w-20 w-full h-full flex flex-row group py-0.5 pr-6.5 !mr-0">
                          <div
                            v-if="sourceIndex === 0"
                            class="source-context flex items-center gap-2 text-gray-800 nc-sidebar-node-title"
                            @contextmenu="setMenuContext('source', source)"
                          >
                            <GeneralBaseLogo class="flex-none min-w-4 !xs:(min-w-4.25 w-4.25 text-sm)" />
                            {{ $t('general.default') }}
                          </div>
                          <div
                            v-else
                            class="source-context flex flex-grow items-center gap-1.75 text-gray-800 min-w-1/20 max-w-full"
                            @contextmenu="setMenuContext('source', source)"
                          >
                            <NcTooltip
                              :tooltip-style="{ 'min-width': 'max-content' }"
                              :overlay-inner-style="{ 'min-width': 'max-content' }"
                              :mouse-leave-delay="0.3"
                              placement="topLeft"
                              trigger="hover"
                            >
                              <template #title>
                                <component :is="getSourceTooltip(source)" />
                              </template>
                              <GeneralBaseLogo
                                :color="getSourceIconColor(source)"
                                class="flex-none min-w-4 !xs:(min-w-4.25 w-4.25 text-sm)"
                              />
                            </NcTooltip>
                            <input
                              v-if="source.id && sourceRenameHelpers[source.id]?.editMode"
                              ref="input"
                              v-model="sourceRenameHelpers[source.id].tempTitle"
                              class="flex-grow leading-1 outline-0 ring-none capitalize !text-inherit !bg-transparent flex-1 mr-4 !text-gray-700"
                              :data-source-rename-input-id="source.id"
                              @click.stop
                              @keydown.enter.stop.prevent
                              @keyup.enter="updateSourceTitle(source.id!)"
                              @keyup.esc="updateSourceTitle(source.id!)"
                              @blur="updateSourceTitle(source.id!)"
                            />
                            <NcTooltip
                              v-else
                              class="nc-sidebar-node-title capitalize text-ellipsis overflow-hidden select-none text-gray-700"
                              :style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
                              show-on-truncate-only
                            >
                              <template #title> {{ source.alias || '' }}</template>
                              <span :data-testid="`nc-sidebar-base-${source.alias}`">
                                {{ source.alias || '' }}
                              </span>
                            </NcTooltip>
                          </div>
                          <div class="flex flex-row items-center gap-x-0.25">
                            <NcDropdown
                              :visible="isBasesOptionsOpen[source!.id!]"
                              :trigger="['click']"
                              @update:visible="isBasesOptionsOpen[source!.id!] = $event"
                            >
                              <NcButton
                                v-e="['c:source:options']"
                                class="nc-sidebar-node-btn"
                                :class="{ '!text-black !opacity-100 !inline-block': isBasesOptionsOpen[source!.id!] }"
                                type="text"
                                size="xxsmall"
                                @click.stop="isBasesOptionsOpen[source!.id!] = !isBasesOptionsOpen[source!.id!]"
                              >
                                <GeneralIcon icon="threeDotHorizontal" class="text-xl w-4.75" />
                              </NcButton>
                              <template #overlay>
                                <NcMenu
                                  class="nc-scrollbar-md"
                                  :style="{
                                    maxHeight: '70vh',
                                    overflow: 'overlay',
                                  }"
                                  @click="isBasesOptionsOpen[source!.id!] = false"
                                >
                                  <NcMenuItem
                                    v-if="isUIAllowed('baseRename')"
                                    data-testid="nc-sidebar-source-rename"
                                    @click="enableEditModeForSource(source.id!)"
                                  >
                                    <GeneralIcon icon="rename" class="group-hover:text-black" />
                                    {{ $t('general.rename') }}
                                  </NcMenuItem>

                                  <NcDivider />

                                  <!-- ERD View -->
                                  <NcMenuItem key="erd" @click="openErdView(source)">
                                    <div v-e="['c:source:erd']" class="flex gap-2 items-center">
                                      <GeneralIcon icon="erd" />
                                      {{ $t('title.relations') }}
                                    </div>
                                  </NcMenuItem>

                                  <DashboardTreeViewBaseOptions
                                    v-if="showBaseOption(source)"
                                    v-model:base="base"
                                    :source="source"
                                  />
                                </NcMenu>
                              </template>
                            </NcDropdown>

                            <NcButton
                              v-if="isUIAllowed('tableCreate', { roles: baseRole, source })"
                              v-e="['c:source:add-table']"
                              type="text"
                              size="xxsmall"
                              class="nc-sidebar-node-btn"
                              :class="{ '!opacity-100 !inline-block': isBasesOptionsOpen[source!.id!] }"
                              @click.stop="openTableCreateDialog(sourceIndex)"
                            >
                              <GeneralIcon icon="plus" class="text-xl leading-5" style="-webkit-text-stroke: 0.15px" />
                            </NcButton>
                          </div>
                        </div>
                      </template>
                      <div
                        ref="menuRefs"
                        :key="`sortable-${source.id}-${source.id && source.id in keys ? keys[source.id] : '0'}`"
                        :nc-source="source.id"
                      >
                        <DashboardTreeViewTableList :base="base" :source-index="sourceIndex" />
                      </div>
                    </a-collapse-panel>
                  </a-collapse>
                </div>
              </div>
            </div>
          </div>
        </template>
      </div>
    </div>
    <template v-if="!isSharedBase" #overlay>
      <NcMenu
        class="!py-0 rounded text-sm"
        :class="{
          '!min-w-62.5': contextMenuTarget.type === 'table',
        }"
      >
        <template v-if="contextMenuTarget.type === 'base' && base.type === 'database'"></template>

        <template v-else-if="contextMenuTarget.type === 'source'"></template>

        <template v-else-if="contextMenuTarget.type === 'table'">
          <NcTooltip>
            <template #title> {{ $t('labels.clickToCopyTableID') }} </template>
            <div
              class="flex items-center justify-between p-2 mx-1.5 rounded-md cursor-pointer hover:bg-gray-100 group"
              @click.stop="onTableIdCopy"
            >
              <div class="flex text-xs font-bold text-gray-500 ml-1">
                {{
                  $t('labels.tableIdColon', {
                    tableId: contextMenuTarget.value?.id,
                  })
                }}
              </div>
              <NcButton class="!group-hover:bg-gray-100" size="xsmall" type="secondary">
                <GeneralIcon v-if="isTableIdCopied" class="max-h-4 min-w-4" icon="check" />
                <GeneralIcon v-else class="max-h-4 min-w-4" else icon="copy" />
              </NcButton>
            </div>
          </NcTooltip>

          <template
            v-if="
              isUIAllowed('tableRename', { source: getSource(contextMenuTarget.value?.source_id) }) ||
              isUIAllowed('tableDelete', { source: getSource(contextMenuTarget.value?.source_id) })
            "
          >
            <NcDivider />
            <NcMenuItem
              v-if="isUIAllowed('tableRename', { source: getSource(contextMenuTarget.value?.source_id) })"
              @click="openRenameTableDialog(contextMenuTarget.value, true)"
            >
              <div v-e="['c:table:rename']" class="nc-base-option-item flex gap-2 items-center">
                <GeneralIcon icon="rename" class="text-gray-700" />
                {{ $t('general.rename') }} {{ $t('objects.table') }}
              </div>
            </NcMenuItem>

            <NcMenuItem
              v-if="
                isUIAllowed('tableDuplicate', { source: getSource(contextMenuTarget.value?.source_id) }) &&
                (contextMenuBase?.is_meta || contextMenuBase?.is_local)
              "
              @click="duplicateTable(contextMenuTarget.value)"
            >
              <div v-e="['c:table:duplicate']" class="nc-base-option-item flex gap-2 items-center">
                <GeneralIcon icon="duplicate" class="text-gray-700" />
                {{ $t('general.duplicate') }} {{ $t('objects.table') }}
              </div>
            </NcMenuItem>
            <NcDivider />
            <NcMenuItem
              v-if="isUIAllowed('tableDelete', { source: getSource(contextMenuTarget.value?.source_id) })"
              class="!hover:bg-red-50"
              @click="tableDelete"
            >
              <div class="nc-base-option-item flex gap-2 items-center text-red-600">
                <GeneralIcon icon="delete" />
                {{ $t('general.delete') }} {{ $t('objects.table') }}
              </div>
            </NcMenuItem>
          </template>
        </template>
      </NcMenu>
    </template>
  </NcDropdown>
  <DlgTableDelete
    v-if="contextMenuTarget.value?.id && base?.id"
    v-model:visible="isTableDeleteDialogVisible"
    :table-id="contextMenuTarget.value?.id"
    :base-id="base?.id"
  />
  <DlgProjectDelete v-model:visible="isProjectDeleteDialogVisible" :base-id="base?.id" />
  <DlgProjectDuplicate v-if="selectedProjectToDuplicate" v-model="isDuplicateDlgOpen" :base="selectedProjectToDuplicate" />
  <GeneralModal v-model:visible="isErdModalOpen" size="large">
    <div class="h-[80vh]">
      <LazyDashboardSettingsErd :source-id="activeBaseId" />
    </div>
  </GeneralModal>
</template>

<style lang="scss" scoped>
:deep(.ant-collapse-header) {
  @apply !mx-0 !pl-8.75 h-7 !xs:(pl-7 h-[3rem]) !pr-0.5 !py-0 hover:bg-gray-200 xs:(hover:bg-gray-50) !rounded-md;

  .ant-collapse-arrow {
    @apply !right-1 !xs:(flex-none border-1 border-gray-200 w-6.5 h-6.5 mr-1);
  }
}

:deep(.ant-collapse-item) {
  @apply h-full;
}

:deep(.ant-collapse-content-box) {
  @apply !px-0 !pb-0 !pt-0.25;
}

:deep(.ant-collapse-header:hover) {
  .nc-sidebar-node-btn {
    @apply !opacity-100 !inline-block;

    &:not(.nc-sidebar-expand) {
      @apply !xs:hidden;
    }
  }
}
</style>
