Restyle InfoPanel to match SelectionPanel (dark glass styling)
Some checks failed
Build Docker Image / build (push) Has been cancelled
Some checks failed
Build Docker Image / build (push) Has been cancelled
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<MapPanel>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Header -->
|
||||
<div class="flex-shrink-0 p-4 border-b border-white/10">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="flex items-center justify-center w-6 h-6 rounded-full"
|
||||
@@ -9,104 +10,49 @@
|
||||
>
|
||||
<Icon :name="entityIcon" size="14" class="text-white" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-base text-base-content">{{ entityName }}</h3>
|
||||
<h3 class="font-semibold text-base text-white">{{ entityName }}</h3>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-ghost btn-xs btn-circle text-base-content/60 hover:text-base-content"
|
||||
@click="emit('close')"
|
||||
>
|
||||
<button class="btn btn-ghost btn-xs btn-circle text-white/60 hover:text-white" @click="emit('close')">
|
||||
<Icon name="lucide:x" size="16" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Loading state -->
|
||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||
<span class="loading loading-spinner loading-md" />
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div v-else-if="entity" class="flex flex-col gap-4">
|
||||
<!-- Entity details card -->
|
||||
<div class="mb-2">
|
||||
<HubCard v-if="entityType === 'hub'" :hub="entity" />
|
||||
<SupplierCard v-else-if="entityType === 'supplier'" :supplier="entity" />
|
||||
<OfferCard v-else-if="entityType === 'offer'" :offer="entity" compact />
|
||||
<!-- Content (scrollable) -->
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<!-- Loading state -->
|
||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||
<span class="loading loading-spinner loading-md text-white" />
|
||||
</div>
|
||||
|
||||
<!-- Tabs for related objects -->
|
||||
<div role="tablist" class="tabs tabs-boxed bg-white/10">
|
||||
<a
|
||||
v-for="tab in availableTabs"
|
||||
:key="tab.id"
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{ 'tab-active': currentTab === tab.id }"
|
||||
@click="currentTab = tab.id"
|
||||
>
|
||||
{{ tab.label }}
|
||||
<span v-if="tab.count" class="ml-1 opacity-70">({{ tab.count }})</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tab content -->
|
||||
<div class="flex flex-col gap-2 min-h-[200px]">
|
||||
<!-- Products tab (only for Offer entity) -->
|
||||
<div v-if="currentTab === 'products' && entityType === 'offer'">
|
||||
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:package" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<ProductCard
|
||||
v-for="product in relatedProducts"
|
||||
:key="product.uuid"
|
||||
:product="product"
|
||||
selectable
|
||||
compact
|
||||
@select="onProductSelect(product)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Content -->
|
||||
<div v-else-if="entity" class="flex flex-col gap-4">
|
||||
<!-- Entity details card -->
|
||||
<div>
|
||||
<HubCard v-if="entityType === 'hub'" :hub="entity" />
|
||||
<SupplierCard v-else-if="entityType === 'supplier'" :supplier="entity" />
|
||||
<OfferCard v-else-if="entityType === 'offer'" :offer="entity" compact />
|
||||
</div>
|
||||
|
||||
<!-- Hubs tab -->
|
||||
<div v-if="currentTab === 'hubs'">
|
||||
<div v-if="relatedHubs.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:warehouse" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.noHubs') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<HubCard
|
||||
v-for="hub in relatedHubs"
|
||||
:key="hub.uuid"
|
||||
:hub="hub"
|
||||
selectable
|
||||
@select="onHubSelect(hub)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Tabs for related objects -->
|
||||
<div role="tablist" class="tabs tabs-boxed bg-white/10">
|
||||
<a
|
||||
v-for="tab in availableTabs"
|
||||
:key="tab.id"
|
||||
role="tab"
|
||||
class="tab text-white/70"
|
||||
:class="{ 'tab-active !text-white !bg-white/20': currentTab === tab.id }"
|
||||
@click="currentTab = tab.id"
|
||||
>
|
||||
{{ tab.label }}
|
||||
<span v-if="tab.count !== undefined" class="ml-1 opacity-70">({{ tab.count }})</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Suppliers tab -->
|
||||
<div v-if="currentTab === 'suppliers'">
|
||||
<div v-if="relatedSuppliers.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:factory" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.noSuppliers') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<SupplierCard
|
||||
v-for="supplier in relatedSuppliers"
|
||||
:key="supplier.uuid"
|
||||
:supplier="supplier"
|
||||
selectable
|
||||
@select="onSupplierSelect(supplier)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Offers tab (two-step for Hub/Supplier) -->
|
||||
<div v-if="currentTab === 'offers'">
|
||||
<!-- Step 1: Select product (for Hub/Supplier) -->
|
||||
<div v-if="!selectedProduct && (entityType === 'hub' || entityType === 'supplier')">
|
||||
<!-- Tab content -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- Products tab (only for Offer entity) -->
|
||||
<div v-if="currentTab === 'products' && entityType === 'offer'">
|
||||
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:package" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
||||
@@ -123,51 +69,106 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Show offers for selected product -->
|
||||
<div v-else-if="selectedProduct">
|
||||
<!-- Back button to products -->
|
||||
<button
|
||||
class="btn btn-sm btn-ghost mb-2 text-white/80 hover:text-white"
|
||||
@click="emit('select-product', null)"
|
||||
>
|
||||
<Icon name="lucide:arrow-left" size="16" />
|
||||
{{ $t('common.back') }}
|
||||
</button>
|
||||
|
||||
<div v-if="relatedOffers.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:shopping-bag" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.empty.noOffers') }}</p>
|
||||
<!-- Hubs tab -->
|
||||
<div v-if="currentTab === 'hubs'">
|
||||
<div v-if="relatedHubs.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:warehouse" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.noHubs') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<OfferResultCard
|
||||
v-for="offer in relatedOffers"
|
||||
:key="offer.uuid"
|
||||
:location-name="offer.locationName || offer.productName"
|
||||
:product-name="offer.productName"
|
||||
:price-per-unit="offer.pricePerUnit ? Number(offer.pricePerUnit) : null"
|
||||
:currency="offer.currency"
|
||||
:unit="offer.unit"
|
||||
:stages="getOfferStages(offer)"
|
||||
@select="onOfferSelect(offer)"
|
||||
<HubCard
|
||||
v-for="hub in relatedHubs"
|
||||
:key="hub.uuid"
|
||||
:hub="hub"
|
||||
selectable
|
||||
@select="onHubSelect(hub)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- For Offer entity type - just show message -->
|
||||
<div v-else class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:info" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.selectProductFirst') }}</p>
|
||||
<!-- Suppliers tab -->
|
||||
<div v-if="currentTab === 'suppliers'">
|
||||
<div v-if="relatedSuppliers.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:factory" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.noSuppliers') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<SupplierCard
|
||||
v-for="supplier in relatedSuppliers"
|
||||
:key="supplier.uuid"
|
||||
:supplier="supplier"
|
||||
selectable
|
||||
@select="onSupplierSelect(supplier)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Offers tab (two-step for Hub/Supplier) -->
|
||||
<div v-if="currentTab === 'offers'">
|
||||
<!-- Step 1: Select product (for Hub/Supplier) -->
|
||||
<div v-if="!selectedProduct && (entityType === 'hub' || entityType === 'supplier')">
|
||||
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:package" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<ProductCard
|
||||
v-for="product in relatedProducts"
|
||||
:key="product.uuid"
|
||||
:product="product"
|
||||
selectable
|
||||
compact
|
||||
@select="onProductSelect(product)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Show offers for selected product -->
|
||||
<div v-else-if="selectedProduct">
|
||||
<!-- Back button to products -->
|
||||
<button
|
||||
class="btn btn-sm btn-ghost mb-2 text-white/80 hover:text-white"
|
||||
@click="emit('select-product', null)"
|
||||
>
|
||||
<Icon name="lucide:arrow-left" size="16" />
|
||||
{{ $t('common.back') }}
|
||||
</button>
|
||||
|
||||
<div v-if="relatedOffers.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:shopping-bag" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.empty.noOffers') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<OfferResultCard
|
||||
v-for="offer in relatedOffers"
|
||||
:key="offer.uuid"
|
||||
:location-name="offer.locationName || offer.productName"
|
||||
:product-name="offer.productName"
|
||||
:price-per-unit="offer.pricePerUnit ? Number(offer.pricePerUnit) : null"
|
||||
:currency="offer.currency"
|
||||
:unit="offer.unit"
|
||||
:stages="getOfferStages(offer)"
|
||||
@select="onOfferSelect(offer)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- For Offer entity type - just show message -->
|
||||
<div v-else class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:info" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.selectProductFirst') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add to filter button -->
|
||||
<button class="btn btn-primary btn-sm mt-2" @click="emit('add-to-filter')">
|
||||
<Icon name="lucide:filter-plus" size="16" />
|
||||
{{ $t('catalog.info.addToFilter') }}
|
||||
</button>
|
||||
<!-- Add to filter button -->
|
||||
<button class="btn btn-primary btn-sm" @click="emit('add-to-filter')">
|
||||
<Icon name="lucide:filter-plus" size="16" />
|
||||
{{ $t('catalog.info.addToFilter') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</MapPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -206,10 +207,10 @@ const currentTab = ref<string>('offers')
|
||||
|
||||
// Entity name
|
||||
const entityName = computed(() => {
|
||||
return props.entity?.name || props.entityId.slice(0, 8) + '...'
|
||||
return props.entity?.name || props.entity?.productName || props.entityId.slice(0, 8) + '...'
|
||||
})
|
||||
|
||||
// Badge color and label
|
||||
// Badge color
|
||||
const badgeColor = computed(() => {
|
||||
if (props.entityType === 'hub') return entityColors.hub
|
||||
if (props.entityType === 'supplier') return entityColors.supplier
|
||||
@@ -217,13 +218,6 @@ const badgeColor = computed(() => {
|
||||
return '#666'
|
||||
})
|
||||
|
||||
const badgeLabel = computed(() => {
|
||||
if (props.entityType === 'hub') return t('catalog.entities.hub')
|
||||
if (props.entityType === 'supplier') return t('catalog.entities.supplier')
|
||||
if (props.entityType === 'offer') return t('catalog.entities.offer')
|
||||
return ''
|
||||
})
|
||||
|
||||
const entityIcon = computed(() => {
|
||||
if (props.entityType === 'hub') return 'lucide:warehouse'
|
||||
if (props.entityType === 'supplier') return 'lucide:factory'
|
||||
|
||||
Reference in New Issue
Block a user