feat(ui): add readonly mode visual badge to sidebar and tab bar
Add visual indicators so users can distinguish readonly connections from read-write connections at a glance: - Sidebar: show a lock icon badge on readonly connection nodes - Tab bar: show a lock icon with tooltip on tabs with readonly connections - Add isConnectionReadonly() helper in tabPresentation.ts - Add readOnlyBadge i18n key for all 6 locales (en, zh-CN, zh-TW, es, it, pt-BR)
This commit is contained in:
1 parent
cb6c09e346
commit
1ca3081c2f
9 files changed
+31
-11
No files matched your search
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, nextTick } from "vue";
|
||||
import type { CSSProperties } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { X, Pin, ChevronDown, Table2, Code2, TableProperties, PencilRuler, KeyRound, Pencil, Package, Check } from "@lucide/vue";
|
||||
import { X, Pin, ChevronDown, Table2, Code2, TableProperties, PencilRuler, KeyRound, Pencil, Package, Check, Lock } from "@lucide/vue";
|
||||
import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
||||
import LightDropdown from "@/components/ui/LightDropdown.vue";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
@@ -10,7 +10,7 @@ import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useTabScroll } from "@/composables/useTabScroll";
|
||||
import { useTabDrag } from "@/composables/useTabDrag";
|
||||
import { connectionColor, tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation";
|
||||
import { connectionColor, isConnectionReadonly, tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation";
|
||||
import { hexToRgba } from "@/lib/color";
|
||||
import type { QueryTab } from "@/types/database";
|
||||
|
||||
@@ -307,6 +307,12 @@ function activateTab(tabId: string) {
|
||||
@blur="commitRenameTab(tab)"
|
||||
/>
|
||||
<span v-else class="min-w-0 truncate flex-1">{{ tabDisplayTitle(tab, t) }}</span>
|
||||
<Tooltip v-if="isConnectionReadonly(tab.connectionId)">
|
||||
<TooltipTrigger as-child>
|
||||
<Lock class="h-3 w-3 text-muted-foreground shrink-0" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("connection.readOnlyBadge") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<button class="inline-flex rounded p-0.5 text-muted-foreground hover:bg-muted-foreground/20 hover:text-foreground focus:opacity-100" :class="tab.pinned ? 'visible text-primary' : 'invisible group-hover:visible'" @click.stop="queryStore.togglePinnedTab(tab.id)">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, nextTick, watch, onBeforeUnmount, inject } from "vue";
|
||||
import { useSqlHighlighter } from "@/composables/useSqlHighlighter";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
Package,
|
||||
Clipboard,
|
||||
UsersRound,
|
||||
Lock,
|
||||
} from "@lucide/vue";
|
||||
import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
@@ -2236,6 +2237,7 @@ const columnComment = computed(() => (props.node.type === "column" && props.node
|
||||
const tableComment = computed(() => ((props.node.type === "table" || props.node.type === "view" || props.node.type === "mongo-collection") && props.node.comment ? props.node.comment : null));
|
||||
const paddingLeft = computed(() => treeItemPaddingLeft(props.depth));
|
||||
const isConnected = computed(() => props.node.type === "connection" && !!props.node.connectionId && connectionStore.connectedIds.has(props.node.connectionId));
|
||||
const isConnectionReadonly = computed(() => props.node.type === "connection" && !!props.node.connectionId && (connectionStore.getConfig(props.node.connectionId)?.read_only ?? false));
|
||||
const canCloseDatabaseConnection = computed(() => props.node.type === "database" && !!props.node.connectionId && props.node.database != null && connectionStore.connectedIds.has(props.node.connectionId));
|
||||
const nodeIconClass = computed(() => {
|
||||
const infoClass = getIconInfo(props.node)?.colorClass;
|
||||
@@ -2976,6 +2978,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
<span v-if="columnComment" class="truncate text-muted-foreground/60 text-[10px] max-w-[40%]">{{ columnComment }}</span>
|
||||
<span v-if="tableComment && !settingsStore.editorSettings.sidebarHideTableComments" class="truncate text-muted-foreground/60 text-[10px] max-w-[25%] group-hover:hidden" :title="tableComment">{{ tableComment }}</span>
|
||||
<span v-if="node.type === 'connection' && node.connectionId && connectionStore.connectedIds.has(node.connectionId)" class="w-1.5 h-1.5 rounded-full bg-green-500 shrink-0" />
|
||||
<Badge v-if="isConnectionReadonly" variant="secondary" class="h-4 px-1.5 text-[10px] gap-0.5"><Lock class="w-2.5 h-2.5" />{{ t("connection.readOnlyBadge") }}</Badge>
|
||||
<ConnectionErrorIndicator v-if="node.type === 'connection'" :connection-id="node.connectionId" trigger-class="h-4 w-4" />
|
||||
<button
|
||||
v-if="canPin"
|
||||
|
||||
Reference in new issue
Block a user