admin felület bekötése. pontosítások2

This commit is contained in:
Roo
2026-06-27 09:08:18 +00:00
parent c75c7b2270
commit 1e6f79ca22
189 changed files with 4624 additions and 1116 deletions

View File

@@ -33,7 +33,7 @@
>
<option value="all">{{ $t('garages.all_tiers') }}</option>
<option
v-for="tier in uniqueTiers"
v-for="tier in availableTierNames"
:key="tier"
:value="tier"
>
@@ -67,7 +67,7 @@
<!-- Content -->
<template v-else>
<!-- P0: Dynamic Stats Cards - based on real org_type data, not fake subscription tiers -->
<!-- P0 HOTFIX: Global Stats Cards - always show unfiltered global counts -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
<div class="bg-slate-800 rounded-xl border border-slate-700 p-4">
<div class="flex items-center gap-3">
@@ -78,7 +78,7 @@
</div>
<div>
<p class="text-xs text-slate-400 uppercase tracking-wider">{{ $t('garages.total_garages') }}</p>
<p class="text-2xl font-bold text-white">{{ garages.length }}</p>
<p class="text-2xl font-bold text-white">{{ globalTotalCount }}</p>
</div>
</div>
</div>
@@ -91,7 +91,7 @@
</div>
<div>
<p class="text-xs text-slate-400 uppercase tracking-wider">{{ $t('garages.private_garages') }}</p>
<p class="text-2xl font-bold text-emerald-400">{{ individualCount }}</p>
<p class="text-2xl font-bold text-emerald-400">{{ globalIndividualCount }}</p>
</div>
</div>
</div>
@@ -104,12 +104,17 @@
</div>
<div>
<p class="text-xs text-slate-400 uppercase tracking-wider">{{ $t('garages.corporate_garages') }}</p>
<p class="text-2xl font-bold text-amber-400">{{ corporateCount }}</p>
<p class="text-2xl font-bold text-amber-400">{{ globalCorporateCount }}</p>
</div>
</div>
</div>
</div>
<!-- P0 HOTFIX: Filtered results indicator -->
<div v-if="isFilterActive" class="mb-4 text-xs text-slate-500">
Szűrt találat: <span class="text-slate-300 font-semibold">{{ garages.length }}</span> db
</div>
<!-- Data Table -->
<div class="bg-slate-800/50 border border-slate-700 rounded-xl overflow-hidden">
<div class="overflow-x-auto">
@@ -143,7 +148,12 @@
{{ getInitials(garageDisplayName(garage)) }}
</div>
<div>
<p class="text-sm font-medium text-white">{{ garageDisplayName(garage) }}</p>
<NuxtLink
:to="'/garages/' + garage.id"
class="text-sm font-medium text-white hover:underline cursor-pointer hover:text-indigo-400 transition-colors"
>
{{ garageDisplayName(garage) }}
</NuxtLink>
<p class="text-xs text-slate-500">{{ garage.city || '—' }} · {{ garage.member_count }} {{ $t('garages.members') }}</p>
</div>
</div>
@@ -402,6 +412,10 @@ const searchQuery = ref('')
const filterType = ref('all')
const selectedPackage = ref('all')
// P0 HOTFIX: Accumulating tier names — never shrinks, only grows.
// This prevents the dropdown from losing options when filtering by tier.
const availableTierNames = ref<string[]>([])
// Modal state
const showSubscriptionModal = ref(false)
const selectedGarage = ref<Garage | null>(null)
@@ -450,20 +464,30 @@ watch(selectedPackage, () => {
// ── Computed ───────────────────────────────────────────────────────
// P0: Dynamic stats based on real org_type from the database
const individualCount = computed(() => garages.value.filter(g => g.org_type === 'individual').length)
const corporateCount = computed(() => garages.value.filter(g => g.org_type !== 'individual').length)
// P0 HOTFIX: Global stats stored from initial unfiltered load
// These NEVER change when filters are applied, so the stat cards
// always reflect the true database totals.
const globalTotalCount = ref(0)
const globalIndividualCount = ref(0)
const globalCorporateCount = ref(0)
// P0: Extract unique subscription tier names for the filter dropdown
const uniqueTiers = computed(() => {
const tiers = new Set<string>()
for (const g of garages.value) {
// P0 HOTFIX: Detect if any filter is active
const isFilterActive = computed(() => {
return searchQuery.value !== '' || filterType.value !== 'all' || selectedPackage.value !== 'all'
})
// P0 HOTFIX: Accumulating tier names — never shrinks, only grows.
// Watcher on garages adds newly discovered tier names to availableTierNames.
// This prevents the dropdown from losing options when filtering by tier.
watch(garages, (newGarages) => {
const currentSet = new Set(availableTierNames.value)
for (const g of newGarages) {
if (g.subscription_tier_name) {
tiers.add(g.subscription_tier_name)
currentSet.add(g.subscription_tier_name)
}
}
return Array.from(tiers).sort()
})
availableTierNames.value = Array.from(currentSet).sort()
}, { immediate: true, deep: true })
// P0: Server-side filtered — the API handles all filtering.
// This computed is now a simple pass-through; the real filtering
@@ -507,6 +531,16 @@ async function fetchGarages() {
headers: getAuthHeaders(),
})
garages.value = response.garages
// P0 HOTFIX: Store global counts from unfiltered API response.
// When no filters are active, the API returns the full dataset,
// so we capture the global totals here. When filters ARE active,
// we keep the previously captured global counts unchanged.
if (!isFilterActive.value) {
globalTotalCount.value = response.total
globalIndividualCount.value = garages.value.filter(g => g.org_type === 'individual').length
globalCorporateCount.value = garages.value.filter(g => g.org_type !== 'individual').length
}
} catch (err: any) {
console.error('[Garages] Failed to fetch garages:', err)
error.value = err?.data?.detail || err?.message || 'Failed to load garages'