Files
FabledCurator/frontend/src/views/ArtistsView.vue
T
bvandeusen 304e8aa878
CI / backend-lint-and-test (push) Successful in 12s
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 29s
CI / integration (push) Successful in 2m57s
fix(ui): mobile responsiveness — nav hamburger + primary-path fixes
The top nav packed brand + health + pipeline chip + ~7 inline links + an
action slot into one flex row, colliding/overflowing on phones (operator:
'almost unusable'). Below 768px the links now fold into a hamburger v-menu;
below 480px the brand text hides (glyph still brands). Plus the primary
browsing path:
- BulkEditorPanel: fixed 320px -> min(320px, 90vw) so it can't swallow the screen.
- GalleryFilterBar: <600px gives search its own full-width row (its 200px
  min-width was jamming the wrapping bar); sort grows.
- GalleryFacetPanel: <480px wraps groups + lets the side-by-side date inputs
  grow full-width.
- ArtistsView grid: minmax(min(440px,100%),1fr) so a card never overflows
  (single column on phones).
- GalleryView: hide the year/month timeline strip <600px.
ImageViewer already stacks its side panel below the image <900px (left as-is).

Secondary surfaces (Posts/Subscriptions filter bars, SubscriptionsTab table,
SeriesReader, PostCard) still need a mobile pass — follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 09:06:33 -04:00

88 lines
2.6 KiB
Vue

<template>
<v-container fluid class="pt-2 pb-6">
<div class="fc-artists__controls">
<v-text-field
v-model="search" density="compact" variant="outlined"
prepend-inner-icon="mdi-magnify" hide-details
placeholder="Search artists" clearable style="max-width: 320px;"
/>
<v-select
v-model="platformModel"
:items="platformItems"
density="compact" variant="outlined" hide-details
placeholder="Platform" clearable style="max-width: 220px;"
/>
</div>
<v-alert v-if="store.error" type="error" variant="tonal" closable class="my-4">
Failed to load: {{ store.error }}
</v-alert>
<v-alert v-else-if="store.isEmpty" type="info" variant="tonal" class="my-4">
No artists match.
</v-alert>
<div class="fc-artists__grid">
<ArtistCard v-for="c in store.cards" :key="c.id" :card="c" />
</div>
<div v-if="store.loading" class="fc-artists__sentinel">
<v-progress-circular indeterminate color="accent" size="28" />
</div>
<div v-else-if="store.hasMore" ref="sentinelEl" class="fc-artists__sentinel" />
</v-container>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue'
import { useArtistDirectoryStore } from '../stores/artistDirectory.js'
import { usePlatformsStore } from '../stores/platforms.js'
import { useInfiniteScroll } from '../composables/useInfiniteScroll.js'
import ArtistCard from '../components/discovery/ArtistCard.vue'
const store = useArtistDirectoryStore()
const platformsStore = usePlatformsStore()
const search = ref('')
const platformModel = ref(null)
const sentinelEl = ref(null)
const platformItems = ref([])
let debounce = null
watch(search, (v) => {
if (debounce) clearTimeout(debounce)
debounce = setTimeout(() => store.setQuery(v || ''), 300)
})
watch(platformModel, (v) => store.setPlatform(v || null))
useInfiniteScroll(sentinelEl, () => {
if (store.hasMore && !store.loading) store.loadMore()
})
onMounted(async () => {
await platformsStore.loadAll()
platformItems.value = (platformsStore.platforms || []).map(p => ({
title: p.key, value: p.key,
}))
store.reset()
})
</script>
<style scoped>
.fc-artists__controls {
display: flex; align-items: center; gap: 16px;
flex-wrap: wrap; margin-bottom: 16px;
}
.fc-artists__grid {
display: grid;
/* min(440px, 100%) so a card never exceeds the viewport — on phones the
min-track collapses to 100% (single column) instead of overflowing. */
grid-template-columns: repeat(auto-fill, minmax(min(440px, 100%), 1fr));
gap: 12px;
}
.fc-artists__sentinel {
display: flex; justify-content: center; padding: 32px 0; min-height: 60px;
}
</style>