Files
kite/frontend/src/views/TopicsView.vue
T
curo1305 2af5b7c313 feat(11-05): visual consistency pass — typography, focus-visible, hover/active states, skeleton cleanup
- Normalize page titles to text-2xl font-semibold (was font-bold in TopicsView, DocumentView, SharedView)
- Normalize section titles to text-lg font-semibold (was text-xl in SettingsPreferencesTab, SettingsAiTab, SettingsCloudTab, BackupCodesDisplay, CloudCredentialModal)
- Normalize panel headings to text-sm font-semibold (SettingsAccountTab, DocumentView)
- Normalize AdminOverviewView page title from text-xl to text-2xl font-semibold
- Replace decorative sidebar skeleton inline styles with static Tailwind width classes (w-12/w-16/w-20)
- Add focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-1 to all interactive buttons across the app
- Add active:bg-* states to primary and secondary buttons for coherent press feedback
- Update nav-link scoped CSS in AppSidebar and AdminSidebar to include active:bg-gray-200 and focus-visible ring
- Add 7 new tests: VISUAL-01 skeleton class invariant, VISUAL-04 focus-visible invariant, VISUAL-03 typography invariant (36 test files, 270 tests pass)
2026-06-16 21:48:56 +02:00

83 lines
3.2 KiB
Vue

<template>
<div class="p-8 max-w-4xl mx-auto">
<!-- Header -->
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-2xl font-semibold text-gray-900">
{{ activeTopic ? activeTopic : 'All Topics' }}
</h2>
<p class="text-gray-500 text-sm mt-0.5">
{{ activeTopic ? `Documents classified under "${activeTopic}"` : 'Manage topics and browse documents by topic' }}
</p>
</div>
<button
v-if="activeTopic"
@click="$router.push('/topics')"
class="text-sm text-indigo-600 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-1 rounded"
>
All Topics
</button>
</div>
<!-- No filter: show topic manager + topic grid -->
<template v-if="!activeTopic">
<TopicManager />
<div class="mt-8">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Browse by Topic</h3>
<div v-if="topicsStore.topics.length === 0" class="text-sm text-gray-400">No topics yet.</div>
<div v-else class="grid grid-cols-2 sm:grid-cols-3 gap-3">
<router-link
v-for="topic in topicsStore.topics"
:key="topic.id"
:to="`/topics/${encodeURIComponent(topic.name)}`"
class="bg-white border border-gray-200 rounded-xl p-4 hover:border-indigo-300 hover:shadow-sm transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-1"
>
<div class="flex items-center gap-2 mb-2">
<span class="w-3 h-3 rounded-full" :style="{ backgroundColor: topic.color }"></span>
<span class="font-medium text-gray-800 text-sm">{{ topic.name }}</span>
</div>
<p class="text-2xl font-bold text-gray-900">{{ topic.doc_count }}</p>
<p class="text-xs text-gray-400">document{{ topic.doc_count !== 1 ? 's' : '' }}</p>
</router-link>
</div>
</div>
</template>
<!-- Filtered by topic: document list -->
<template v-else>
<div v-if="docsStore.loading" class="text-sm text-gray-400">Loading</div>
<div v-else-if="docsStore.documents.length === 0" class="text-center py-12 text-gray-400">
No documents under this topic yet.
</div>
<div v-else class="grid gap-3">
<DocumentCard v-for="doc in docsStore.documents" :key="doc.id" :doc="doc" />
</div>
</template>
</div>
</template>
<script setup>
import { computed, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import TopicManager from '../components/topics/TopicManager.vue'
import DocumentCard from '../components/documents/DocumentCard.vue'
import { useTopicsStore } from '../stores/topics.js'
import { useDocumentsStore } from '../stores/documents.js'
const route = useRoute()
const topicsStore = useTopicsStore()
const docsStore = useDocumentsStore()
const activeTopic = computed(() => route.params.name ? decodeURIComponent(route.params.name) : null)
function loadDocs() {
if (activeTopic.value) {
docsStore.fetchDocuments({ topic: activeTopic.value })
}
}
onMounted(loadDocs)
watch(activeTopic, loadDocs)
</script>