refactor(ui): replace title attrs with Tooltip on interactive elements

Wrap 'Mark done', 'Edit project', 'Generate AI summary' buttons and inline-editable OMG spans with Tooltip component; replace priority dot title attr in KanbanCard; also adds focus-visible ring to 'Mark done' button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-13 11:20:54 +01:00
parent ebd5616498
commit b8329904e8
4 changed files with 18 additions and 8 deletions

View file

@ -1,4 +1,5 @@
<script setup lang="ts">
import Tooltip from '@/components/ui/Tooltip.vue'
import type { Task } from '@/types'
const props = defineProps<{ task: Task; dragging: boolean }>()
@ -32,10 +33,11 @@ const statusLabel: Record<string, string> = {
>
<div class="flex items-start gap-2">
<!-- Priority dot -->
<Tooltip :content="`Priority ${task.priority}`">
<span
:class="['mt-1.5 h-2 w-2 rounded-full shrink-0', priorityDot[task.priority] ?? 'bg-slate-300']"
:title="`Priority ${task.priority}`"
/>
</Tooltip>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-slate-800 leading-snug line-clamp-2">{{ task.title }}</p>

View file

@ -11,6 +11,7 @@ import KpiRow from '@/components/dashboard/KpiRow.vue'
import TimelineChart from '@/components/dashboard/TimelineChart.vue'
import ToolUsageList from '@/components/dashboard/ToolUsageList.vue'
import ProjectBreakdown from '@/components/dashboard/ProjectBreakdown.vue'
import Tooltip from '@/components/ui/Tooltip.vue'
import { useTasksStore } from '@/stores/tasks'
import { useDevopsStore } from '@/stores/devops'
import { isoDateStr } from '@/lib/utils'
@ -147,18 +148,19 @@ onMounted(async () => {
:key="task.id"
class="flex items-center gap-2 group py-1"
>
<Tooltip content="Mark done">
<button
class="h-4 w-4 shrink-0 rounded border border-border/70 group-hover:border-primary transition-colors flex items-center justify-center"
class="h-4 w-4 shrink-0 rounded border border-border/70 group-hover:border-primary transition-colors flex items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
:class="{ 'opacity-50': completingTask === task.id }"
:disabled="!!completingTask"
@click="quickComplete(task.id)"
title="Mark done"
>
<svg v-if="completingTask === task.id" class="h-3 w-3 animate-spin text-primary" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
</svg>
</button>
</Tooltip>
<span class="flex-1 text-xs text-foreground truncate">{{ task.title }}</span>
<span
v-if="task.status === 'doing'"

View file

@ -191,12 +191,13 @@ function cancelInline() {
@keydown.enter="commitInline(entry)"
@keydown.escape="cancelInline"
/>
<Tooltip content="Double-click to edit">
<span
v-else
class="text-sm font-medium text-foreground cursor-pointer hover:text-primary transition-colors"
title="Double-click to edit"
@dblclick="startInline(entry, 'name')"
>{{ entry.name }}</span>
</Tooltip>
</div>
<!-- Client -->
@ -211,13 +212,14 @@ function cancelInline() {
@keydown.enter="commitInline(entry)"
@keydown.escape="cancelInline"
/>
<Tooltip content="Double-click to edit">
<span
v-else
class="text-sm text-muted-foreground cursor-pointer hover:text-foreground transition-colors"
:class="entry.client ? '' : 'italic opacity-40'"
title="Double-click to edit"
@dblclick="startInline(entry, 'client')"
>{{ entry.client || 'No client' }}</span>
</Tooltip>
</div>
<!-- Job # -->
@ -232,13 +234,14 @@ function cancelInline() {
@keydown.enter="commitInline(entry)"
@keydown.escape="cancelInline"
/>
<Tooltip content="Double-click to edit">
<span
v-else
class="text-sm tabular-nums cursor-pointer hover:text-foreground transition-colors"
:class="entry.job_number ? 'text-foreground' : 'text-muted-foreground/40 italic'"
title="Double-click to edit"
@dblclick="startInline(entry, 'job_number')"
>{{ entry.job_number || '—' }}</span>
</Tooltip>
</div>
<!-- Actions -->

View file

@ -11,6 +11,7 @@ import Skeleton from '@/components/ui/Skeleton.vue'
import Input from '@/components/ui/Input.vue'
import Button from '@/components/ui/Button.vue'
import EmptyState from '@/components/ui/EmptyState.vue'
import Tooltip from '@/components/ui/Tooltip.vue'
import { formatDuration, formatDateTime } from '@/lib/utils'
import { useAuthStore } from '@/stores/auth'
import { toast } from 'vue-sonner'
@ -142,15 +143,16 @@ async function summarizeSession(sessionId: string) {
>
All time
</button>
<Tooltip content="Edit project">
<button
class="p-1 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/40 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
title="Edit project"
@click="openEdit"
>
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
</Tooltip>
</div>
<div class="flex items-center gap-3 mt-1 flex-wrap">
<span v-if="data.project.client" class="text-sm text-muted-foreground">
@ -369,10 +371,10 @@ async function summarizeSession(sessionId: string) {
<p class="text-xs font-medium text-foreground">{{ formatDuration(session.active_hours) }}</p>
<p class="text-xs text-muted-foreground">{{ session.commits.length }} commits</p>
</div>
<Tooltip content="Generate AI summary">
<button
class="flex h-8 w-8 items-center justify-center rounded text-muted-foreground hover:text-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
:class="{ 'opacity-50 cursor-not-allowed': summarizingSession === session.id }"
title="Generate AI summary"
@click="summarizeSession(session.id)"
>
<svg
@ -395,6 +397,7 @@ async function summarizeSession(sessionId: string) {
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
</svg>
</button>
</Tooltip>
</div>
</div>
</div>