fix: pin home capsule flow and switch explore panel to white left dock
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Header with close button -->
|
||||
<div class="flex-shrink-0 p-4 border-b border-white/10">
|
||||
<div class="flex-shrink-0 p-4 border-b border-base-300">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
@@ -10,19 +10,19 @@
|
||||
>
|
||||
<Icon :name="entityIcon" size="14" class="text-white" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-base text-white">{{ entityName }}</h3>
|
||||
<h3 class="font-semibold text-base text-base-content">{{ entityName }}</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
v-if="(entityType === 'hub' || entityType === 'supplier') && entity?.uuid"
|
||||
class="rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
|
||||
class="rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 transition-transform hover:scale-105"
|
||||
@click="emit('pin', entityType, { uuid: entity?.uuid, name: entity?.name })"
|
||||
aria-label="Pin"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-xs btn-circle text-white/60 hover:text-white" @click="emit('close')">
|
||||
<button class="btn btn-ghost btn-xs btn-circle text-base-content/60 hover:text-base-content" @click="emit('close')">
|
||||
<Icon name="lucide:x" size="16" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -33,7 +33,7 @@
|
||||
<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" />
|
||||
<span class="loading loading-spinner loading-md text-base-content" />
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
@@ -41,13 +41,13 @@
|
||||
<!-- Entity Info Header (text, not card) -->
|
||||
<div class="mb-2">
|
||||
<!-- Location for hub/supplier -->
|
||||
<p v-if="entityLocation" class="text-sm text-white/70 flex items-center gap-1">
|
||||
<p v-if="entityLocation" class="text-sm text-base-content/70 flex items-center gap-1">
|
||||
<Icon name="lucide:map-pin" size="14" />
|
||||
{{ entityLocation }}
|
||||
</p>
|
||||
|
||||
<!-- Price for offer -->
|
||||
<p v-if="entityType === 'offer' && entity?.pricePerUnit" class="text-sm text-white/70 flex items-center gap-1">
|
||||
<p v-if="entityType === 'offer' && entity?.pricePerUnit" class="text-sm text-base-content/70 flex items-center gap-1">
|
||||
<Icon name="lucide:tag" size="14" />
|
||||
{{ formatPrice(entity.pricePerUnit) }} {{ entity.currency || 'RUB' }}/{{ entity.unit || 't' }}
|
||||
</p>
|
||||
@@ -65,8 +65,8 @@
|
||||
</div>
|
||||
|
||||
<!-- KYC Teaser Section (for supplier) -->
|
||||
<section v-if="entityType === 'supplier' && kycTeaser" class="bg-white/5 rounded-lg p-3">
|
||||
<h3 class="text-sm font-semibold text-white/80 mb-2 flex items-center gap-2">
|
||||
<section v-if="entityType === 'supplier' && kycTeaser" class="bg-base-200/50 rounded-lg p-3">
|
||||
<h3 class="text-sm font-semibold text-base-content/80 mb-2 flex items-center gap-2">
|
||||
<Icon name="lucide:shield-check" size="16" />
|
||||
{{ $t('catalog.info.kycTeaser') }}
|
||||
</h3>
|
||||
@@ -74,19 +74,19 @@
|
||||
<div class="flex flex-col gap-2 text-sm">
|
||||
<!-- Company Type -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-white/60">{{ $t('catalog.info.companyType') }}</span>
|
||||
<span class="text-white">{{ kycTeaser.companyType }}</span>
|
||||
<span class="text-base-content/60">{{ $t('catalog.info.companyType') }}</span>
|
||||
<span class="text-base-content">{{ kycTeaser.companyType }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Registration Year -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-white/60">{{ $t('catalog.info.registrationYear') }}</span>
|
||||
<span class="text-white">{{ kycTeaser.registrationYear }}</span>
|
||||
<span class="text-base-content/60">{{ $t('catalog.info.registrationYear') }}</span>
|
||||
<span class="text-base-content">{{ kycTeaser.registrationYear }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-white/60">{{ $t('catalog.info.status') }}</span>
|
||||
<span class="text-base-content/60">{{ $t('catalog.info.status') }}</span>
|
||||
<span :class="kycTeaser.isActive ? 'text-success' : 'text-error'">
|
||||
{{ kycTeaser.isActive ? $t('catalog.info.active') : $t('catalog.info.inactive') }}
|
||||
</span>
|
||||
@@ -94,8 +94,8 @@
|
||||
|
||||
<!-- Sources Count -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-white/60">{{ $t('catalog.info.sourcesCount') }}</span>
|
||||
<span class="text-white">{{ kycTeaser.sourcesCount }}</span>
|
||||
<span class="text-base-content/60">{{ $t('catalog.info.sourcesCount') }}</span>
|
||||
<span class="text-base-content">{{ kycTeaser.sourcesCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -111,14 +111,14 @@
|
||||
|
||||
<!-- Products Section (for hub/supplier) - hide when product selected -->
|
||||
<section v-if="(entityType === 'hub' || entityType === 'supplier') && !selectedProduct">
|
||||
<h3 class="text-sm font-semibold text-white/80 mb-2 flex items-center gap-2">
|
||||
<h3 class="text-sm font-semibold text-base-content/80 mb-2 flex items-center gap-2">
|
||||
<Icon name="lucide:package" size="16" />
|
||||
{{ productsSectionTitle }}
|
||||
<span v-if="loadingProducts" class="loading loading-spinner loading-xs" />
|
||||
<span v-else-if="relatedProducts.length > 0" class="text-white/50">({{ relatedProducts.length }})</span>
|
||||
<span v-else-if="relatedProducts.length > 0" class="text-base-content/50">({{ relatedProducts.length }})</span>
|
||||
</h3>
|
||||
|
||||
<div v-if="!loadingProducts && relatedProducts.length === 0" class="text-white/50 text-sm py-2">
|
||||
<div v-if="!loadingProducts && relatedProducts.length === 0" class="text-base-content/50 text-sm py-2">
|
||||
{{ $t('catalog.empty.noProducts') }}
|
||||
</div>
|
||||
<div v-else-if="!loadingProducts" class="flex flex-col gap-2">
|
||||
@@ -134,12 +134,12 @@
|
||||
@select="onProductSelect(product)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'product', product)"
|
||||
aria-label="Pin product"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -148,14 +148,14 @@
|
||||
<!-- Offers Section (after product selected) -->
|
||||
<section v-if="(entityType === 'hub' || entityType === 'supplier') && selectedProduct">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-sm font-semibold text-white/80 flex items-center gap-2">
|
||||
<h3 class="text-sm font-semibold text-base-content/80 flex items-center gap-2">
|
||||
<Icon name="lucide:shopping-bag" size="16" />
|
||||
{{ $t('catalog.headers.offers') }}
|
||||
<span v-if="loadingOffers" class="loading loading-spinner loading-xs" />
|
||||
<span v-else-if="offersWithPrice.length > 0" class="text-white/50">({{ offersWithPrice.length }})</span>
|
||||
<span v-else-if="offersWithPrice.length > 0" class="text-base-content/50">({{ offersWithPrice.length }})</span>
|
||||
</h3>
|
||||
<button
|
||||
class="flex items-center gap-2 px-2 py-1 rounded-full border border-white/15 bg-white/10 text-xs text-white/80 hover:bg-white/20 transition-colors"
|
||||
class="flex items-center gap-2 px-2 py-1 rounded-full border border-base-300/70 bg-base-200/70 text-xs text-base-content/80 hover:bg-base-200 transition-colors"
|
||||
@click="emit('select-product', null)"
|
||||
>
|
||||
<Icon name="lucide:package" size="12" />
|
||||
@@ -164,7 +164,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="!loadingOffers && offersWithPrice.length === 0" class="text-white/50 text-sm py-2">
|
||||
<div v-if="!loadingOffers && offersWithPrice.length === 0" class="text-base-content/50 text-sm py-2">
|
||||
{{ $t('catalog.empty.noOffers') }}
|
||||
</div>
|
||||
<div v-else-if="!loadingOffers" class="flex flex-col gap-2">
|
||||
@@ -187,14 +187,14 @@
|
||||
|
||||
<!-- Suppliers Section (for hub only) -->
|
||||
<section v-if="entityType === 'hub' && !selectedProduct">
|
||||
<h3 class="text-sm font-semibold text-white/80 mb-2 flex items-center gap-2">
|
||||
<h3 class="text-sm font-semibold text-base-content/80 mb-2 flex items-center gap-2">
|
||||
<Icon name="lucide:factory" size="16" />
|
||||
{{ $t('catalog.info.suppliersNearby') }}
|
||||
<span v-if="loadingSuppliers" class="loading loading-spinner loading-xs" />
|
||||
<span v-else-if="relatedSuppliers.length > 0" class="text-white/50">({{ relatedSuppliers.length }})</span>
|
||||
<span v-else-if="relatedSuppliers.length > 0" class="text-base-content/50">({{ relatedSuppliers.length }})</span>
|
||||
</h3>
|
||||
|
||||
<div v-if="!loadingSuppliers && relatedSuppliers.length === 0" class="text-white/50 text-sm py-2">
|
||||
<div v-if="!loadingSuppliers && relatedSuppliers.length === 0" class="text-base-content/50 text-sm py-2">
|
||||
{{ $t('catalog.info.noSuppliers') }}
|
||||
</div>
|
||||
<div v-else-if="!loadingSuppliers" class="flex flex-col gap-2">
|
||||
@@ -209,12 +209,12 @@
|
||||
@select="onSupplierSelect(supplier)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'supplier', supplier)"
|
||||
aria-label="Pin supplier"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,25 +222,25 @@
|
||||
|
||||
<!-- Hubs Section (for supplier/offer) -->
|
||||
<section v-if="entityType === 'supplier' || entityType === 'offer'">
|
||||
<h3 class="text-sm font-semibold text-white/80 mb-2 flex items-center gap-2">
|
||||
<h3 class="text-sm font-semibold text-base-content/80 mb-2 flex items-center gap-2">
|
||||
<Icon name="lucide:warehouse" size="16" />
|
||||
{{ $t('catalog.info.nearestHubs') }}
|
||||
<span v-if="loadingHubs" class="loading loading-spinner loading-xs" />
|
||||
<span v-else-if="relatedHubs.length > 0" class="text-white/50">({{ relatedHubs.length }})</span>
|
||||
<span v-else-if="relatedHubs.length > 0" class="text-base-content/50">({{ relatedHubs.length }})</span>
|
||||
</h3>
|
||||
|
||||
<div v-if="!loadingHubs && relatedHubs.length === 0" class="text-white/50 text-sm py-2">
|
||||
<div v-if="!loadingHubs && relatedHubs.length === 0" class="text-base-content/50 text-sm py-2">
|
||||
{{ $t('catalog.info.noHubs') }}
|
||||
</div>
|
||||
<div v-else-if="!loadingHubs" class="space-y-4">
|
||||
<template v-if="railHubs.length">
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<Card padding="small" class="border border-white/10 bg-white/5">
|
||||
<Card padding="small" class="border border-base-300 bg-base-200/50">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center">
|
||||
<Icon name="lucide:train-front" size="16" class="text-white/80" />
|
||||
<div class="w-8 h-8 rounded-lg bg-base-200/70 flex items-center justify-center">
|
||||
<Icon name="lucide:train-front" size="16" class="text-base-content/80" />
|
||||
</div>
|
||||
<div class="text-sm text-white/80">{{ $t('catalog.info.railHubs') }}</div>
|
||||
<div class="text-sm text-base-content/80">{{ $t('catalog.info.railHubs') }}</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div
|
||||
@@ -255,12 +255,12 @@
|
||||
@select="onHubSelect(hub)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'hub', hub)"
|
||||
aria-label="Pin hub"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,12 +268,12 @@
|
||||
|
||||
<template v-if="seaHubs.length">
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<Card padding="small" class="border border-white/10 bg-white/5">
|
||||
<Card padding="small" class="border border-base-300 bg-base-200/50">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center">
|
||||
<Icon name="lucide:ship" size="16" class="text-white/80" />
|
||||
<div class="w-8 h-8 rounded-lg bg-base-200/70 flex items-center justify-center">
|
||||
<Icon name="lucide:ship" size="16" class="text-base-content/80" />
|
||||
</div>
|
||||
<div class="text-sm text-white/80">{{ $t('catalog.info.seaHubs') }}</div>
|
||||
<div class="text-sm text-base-content/80">{{ $t('catalog.info.seaHubs') }}</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div
|
||||
@@ -288,12 +288,12 @@
|
||||
@select="onHubSelect(hub)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'hub', hub)"
|
||||
aria-label="Pin hub"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Header -->
|
||||
<div class="flex-shrink-0 p-4 border-b border-white/10">
|
||||
<div class="flex-shrink-0 p-4 border-b border-base-300">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="font-semibold text-base text-white">{{ $t('catalog.headers.offers') }}</h3>
|
||||
<h3 class="font-semibold text-base text-base-content">{{ $t('catalog.headers.offers') }}</h3>
|
||||
<span class="badge badge-neutral">{{ totalOffers }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,10 +11,10 @@
|
||||
<!-- Content (scrollable) -->
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||
<span class="loading loading-spinner loading-md text-white" />
|
||||
<span class="loading loading-spinner loading-md text-base-content" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="offersWithPrice.length === 0" class="text-center py-8 text-white/60">
|
||||
<div v-else-if="offersWithPrice.length === 0" class="text-center py-8 text-base-content/60">
|
||||
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
||||
<p>{{ $t('catalog.empty.noOffers') }}</p>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Header -->
|
||||
<div class="flex-shrink-0 p-4 border-b border-white/10">
|
||||
<div class="flex-shrink-0 p-4 border-b border-base-300">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="font-semibold text-base text-white">{{ title }}</h3>
|
||||
<button class="btn btn-ghost btn-xs btn-circle text-white/60 hover:text-white" @click="emit('close')">
|
||||
<h3 class="font-semibold text-base text-base-content">{{ title }}</h3>
|
||||
<button class="btn btn-ghost btn-xs btn-circle text-base-content/60 hover:text-base-content" @click="emit('close')">
|
||||
<Icon name="lucide:x" size="16" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -13,10 +13,10 @@
|
||||
<!-- Content (scrollable) -->
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||
<span class="loading loading-spinner loading-md text-white" />
|
||||
<span class="loading loading-spinner loading-md text-base-content" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="items.length === 0" class="text-center py-8 text-white/60">
|
||||
<div v-else-if="items.length === 0" class="text-center py-8 text-base-content/60">
|
||||
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
||||
<p>{{ $t('catalog.empty.noResults') }}</p>
|
||||
</div>
|
||||
@@ -38,12 +38,12 @@
|
||||
@select="onSelect(item)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'product', item)"
|
||||
aria-label="Pin product"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -63,12 +63,12 @@
|
||||
@select="onSelect(item)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'hub', item)"
|
||||
aria-label="Pin hub"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -88,12 +88,12 @@
|
||||
@select="onSelect(item)"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full glass-bright border border-white/30 shadow-lg p-1.5 hover:scale-105"
|
||||
class="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-base-100 border border-base-300 shadow-lg p-1.5 hover:scale-105"
|
||||
@click.stop="emit('pin', 'supplier', item)"
|
||||
aria-label="Pin supplier"
|
||||
title="Pin"
|
||||
>
|
||||
<Icon name="lucide:pin" size="16" class="text-white" />
|
||||
<Icon name="lucide:pin" size="16" class="text-base-content" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -104,7 +104,7 @@
|
||||
ref="loadMoreSentinel"
|
||||
class="flex items-center justify-center py-4"
|
||||
>
|
||||
<span v-if="loadingMore" class="loading loading-spinner loading-sm text-white/60" />
|
||||
<span v-if="loadingMore" class="loading loading-spinner loading-sm text-base-content/60" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -102,10 +102,11 @@
|
||||
:class="[
|
||||
isHeroLayout ? 'w-full max-w-none justify-start' : 'max-w-2xl justify-center'
|
||||
]"
|
||||
:style="centerStyle"
|
||||
>
|
||||
<!-- Hero slot for home page title -->
|
||||
<slot name="hero" />
|
||||
<div :style="heroTitleStyle">
|
||||
<slot name="hero" />
|
||||
</div>
|
||||
|
||||
<!-- Client Area tabs -->
|
||||
<template v-if="isClientArea">
|
||||
@@ -419,10 +420,13 @@ const props = withDefaults(defineProps<{
|
||||
collapseProgress?: number
|
||||
// Home scroll position for floating center capsule
|
||||
heroScrollY?: number
|
||||
// Initial hero height for stable landing capsule start position
|
||||
heroBaseHeight?: number
|
||||
}>(), {
|
||||
height: 100,
|
||||
collapseProgress: 1,
|
||||
heroScrollY: 0
|
||||
heroScrollY: 0,
|
||||
heroBaseHeight: 0
|
||||
})
|
||||
|
||||
defineEmits([
|
||||
@@ -527,7 +531,6 @@ const getTokenIcon = (type: string) => {
|
||||
|
||||
const isHeroLayout = computed(() => props.isHomePage && !props.isClientArea)
|
||||
const topRowHeight = 100
|
||||
const LANDING_CAPSULE_TOP_START = 450
|
||||
const LANDING_CAPSULE_TOP_STOP = 2
|
||||
|
||||
const rowStyle = computed(() => {
|
||||
@@ -537,7 +540,7 @@ const rowStyle = computed(() => {
|
||||
return { height: `${props.height}px` }
|
||||
})
|
||||
|
||||
const centerStyle = computed(() => {
|
||||
const heroTitleStyle = computed(() => {
|
||||
if (!isHeroLayout.value) return {}
|
||||
const heroHeight = props.height || topRowHeight
|
||||
const minTop = 0
|
||||
@@ -549,15 +552,21 @@ const centerStyle = computed(() => {
|
||||
|
||||
const isFloatingHomeCapsule = computed(() => isHeroLayout.value)
|
||||
|
||||
const landingCapsuleTopStart = computed(() => {
|
||||
const base = props.heroBaseHeight || props.height || 0
|
||||
if (!base) return 420
|
||||
return Math.max(300, Math.min(520, Math.round(base * 0.52)))
|
||||
})
|
||||
|
||||
const landingCapsuleTop = computed(() => {
|
||||
if (!isFloatingHomeCapsule.value) return LANDING_CAPSULE_TOP_STOP
|
||||
const y = Math.max(0, props.heroScrollY || 0)
|
||||
return Math.max(LANDING_CAPSULE_TOP_STOP, LANDING_CAPSULE_TOP_START - y)
|
||||
return Math.max(LANDING_CAPSULE_TOP_STOP, landingCapsuleTopStart.value - y)
|
||||
})
|
||||
|
||||
const searchCapsuleClass = computed(() => {
|
||||
if (!isFloatingHomeCapsule.value) return 'w-full'
|
||||
return 'w-full lg:fixed lg:left-1/2 lg:z-40 lg:w-[min(1120px,calc(100%-1.5rem))] lg:-translate-x-1/2 transition-[top] duration-200 ease-out'
|
||||
return 'w-full lg:absolute lg:left-1/2 lg:z-20 lg:w-[min(1120px,calc(100%-1.5rem))] lg:-translate-x-1/2 transition-[top] duration-200 ease-out'
|
||||
})
|
||||
|
||||
const searchCapsuleStyle = computed(() => {
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
<!-- Filter by bounds checkbox (LEFT, next to panel when open) - only in selection mode -->
|
||||
<label
|
||||
v-if="selectMode !== null"
|
||||
class="absolute top-[116px] left-[calc(1rem+32rem+1rem)] z-20 hidden lg:flex items-center gap-2 map-chip rounded-full px-3 py-1.5 cursor-pointer text-base-content text-sm hover:bg-base-200 transition-colors"
|
||||
class="absolute top-[116px] z-20 hidden lg:flex items-center gap-2 map-chip rounded-full px-3 py-1.5 cursor-pointer text-base-content text-sm hover:bg-base-200 transition-colors"
|
||||
:style="boundsFilterStyle"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -101,10 +102,10 @@
|
||||
<Transition name="slide-left">
|
||||
<div
|
||||
v-if="isPanelOpen"
|
||||
class="absolute top-[116px] left-4 bottom-4 z-30 max-w-[calc(100vw-2rem)] hidden lg:block"
|
||||
class="absolute top-[116px] left-0 bottom-0 z-30 max-w-[calc(100vw-1rem)] hidden lg:block"
|
||||
:class="panelWidth"
|
||||
>
|
||||
<div class="bg-neutral text-neutral-content border border-neutral/70 rounded-[1.1rem] shadow-2xl h-full flex flex-col">
|
||||
<div class="bg-base-100 text-base-content border border-base-300 border-l-0 rounded-none rounded-tr-[1.1rem] rounded-bl-[1.1rem] shadow-2xl h-full flex flex-col">
|
||||
<slot name="panel" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,7 +163,7 @@
|
||||
<Transition name="slide-up">
|
||||
<div
|
||||
v-if="isPanelOpen"
|
||||
class="bg-neutral text-neutral-content rounded-t-3xl border-t border-neutral/70 shadow-[0_-8px_40px_rgba(0,0,0,0.35)] transition-all duration-300 h-[60vh]"
|
||||
class="bg-base-100 text-base-content rounded-t-3xl border-t border-base-300 shadow-[0_-8px_40px_rgba(0,0,0,0.12)] transition-all duration-300 h-[60vh]"
|
||||
>
|
||||
<!-- Drag handle / close -->
|
||||
<div
|
||||
@@ -212,11 +213,16 @@ const panelWidthPx = computed(() => {
|
||||
|
||||
const fitPaddingLeft = computed(() => {
|
||||
if (!isPanelOpen.value || !isDesktop.value || panelWidthPx.value === 0) return 0
|
||||
const leftInset = 16
|
||||
const leftInset = 0
|
||||
const rightInset = 16
|
||||
return leftInset + panelWidthPx.value + rightInset
|
||||
})
|
||||
|
||||
const boundsFilterStyle = computed(() => {
|
||||
if (!isDesktop.value || panelWidthPx.value === 0) return { left: '1rem' }
|
||||
return { left: `${panelWidthPx.value + 16}px` }
|
||||
})
|
||||
|
||||
// Open panel based on current mapViewMode
|
||||
const openPanel = () => {
|
||||
const newSelectMode = mapViewMode.value === 'hubs' ? 'hub'
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
:height="isHomePage ? heroHeight : 100"
|
||||
:collapse-progress="isHomePage ? collapseProgress : 1"
|
||||
:hero-scroll-y="isHomePage ? heroScrollY : 0"
|
||||
:hero-base-height="isHomePage ? heroBaseHeight : 0"
|
||||
:session-checked="sessionChecked"
|
||||
:logged-in="isLoggedIn"
|
||||
:user-avatar-svg="userAvatarSvg"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
>
|
||||
<template #panel>
|
||||
<!-- Panel header -->
|
||||
<div class="p-4 border-b border-white/10 flex-shrink-0">
|
||||
<div class="p-4 border-b border-base-300 flex-shrink-0">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span class="font-semibold">{{ t('cabinetNav.addresses') }}</span>
|
||||
</div>
|
||||
@@ -26,14 +26,14 @@
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
:placeholder="t('common.search')"
|
||||
class="input input-sm w-full bg-white/10 border-white/20 text-white placeholder:text-white/50"
|
||||
class="input input-sm w-full bg-base-200 border-base-300 text-base-content placeholder:text-base-content/50"
|
||||
/>
|
||||
<Icon name="lucide:search" size="16" class="absolute right-3 top-1/2 -translate-y-1/2 text-white/50" />
|
||||
<Icon name="lucide:search" size="16" class="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/50" />
|
||||
</div>
|
||||
|
||||
<!-- Add button -->
|
||||
<NuxtLink :to="localePath('/clientarea/addresses/new')">
|
||||
<button class="btn btn-sm w-full bg-white/10 border-white/20 text-white hover:bg-white/20">
|
||||
<button class="btn btn-sm w-full bg-base-200 border-base-300 text-base-content hover:bg-base-200">
|
||||
<Icon name="lucide:plus" size="14" class="mr-1" />
|
||||
{{ t('profileAddresses.actions.add') }}
|
||||
</button>
|
||||
@@ -46,7 +46,7 @@
|
||||
<div
|
||||
v-for="item in displayItems"
|
||||
:key="item.uuid"
|
||||
class="bg-white/10 rounded-lg p-3 hover:bg-white/20 transition-colors cursor-pointer"
|
||||
class="bg-base-200 rounded-lg p-3 hover:bg-base-200 transition-colors cursor-pointer"
|
||||
:class="{ 'ring-2 ring-emerald-500': selectedAddressId === item.uuid }"
|
||||
@click="selectedAddressId = item.uuid"
|
||||
@mouseenter="hoveredAddressId = item.uuid"
|
||||
@@ -56,7 +56,7 @@
|
||||
<span class="text-xl">{{ isoToEmoji(item.countryCode) }}</span>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-semibold text-sm truncate">{{ item.name }}</div>
|
||||
<div class="text-xs text-white/60 line-clamp-2">{{ item.address }}</div>
|
||||
<div class="text-xs text-base-content/60 line-clamp-2">{{ item.address }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,9 +65,9 @@
|
||||
<div class="text-center py-8">
|
||||
<div class="text-3xl mb-2">📍</div>
|
||||
<div class="font-semibold text-sm mb-1">{{ t('profileAddresses.empty.title') }}</div>
|
||||
<div class="text-xs text-white/60 mb-3">{{ t('profileAddresses.empty.description') }}</div>
|
||||
<div class="text-xs text-base-content/60 mb-3">{{ t('profileAddresses.empty.description') }}</div>
|
||||
<NuxtLink :to="localePath('/clientarea/addresses/new')">
|
||||
<button class="btn btn-sm bg-white/10 border-white/20 text-white hover:bg-white/20">
|
||||
<button class="btn btn-sm bg-base-200 border-base-300 text-base-content hover:bg-base-200">
|
||||
<Icon name="lucide:plus" size="14" class="mr-1" />
|
||||
{{ t('profileAddresses.empty.cta') }}
|
||||
</button>
|
||||
@@ -77,8 +77,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="p-3 border-t border-white/10 flex-shrink-0">
|
||||
<span class="text-xs text-white/50">{{ displayItems.length }} {{ t('catalog.of') }} {{ items.length }}</span>
|
||||
<div class="p-3 border-t border-base-300 flex-shrink-0">
|
||||
<span class="text-xs text-base-content/50">{{ displayItems.length }} {{ t('catalog.of') }} {{ items.length }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</CatalogPage>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
>
|
||||
<template #panel>
|
||||
<!-- Panel header -->
|
||||
<div class="p-4 border-b border-white/10 flex-shrink-0">
|
||||
<div class="p-4 border-b border-base-300 flex-shrink-0">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-indigo-500/20 flex items-center justify-center">
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-sm">{{ t('cabinetNav.orders') }}</span>
|
||||
<div class="text-xs text-white/50">{{ filteredItems.length }} {{ t('orders.total', 'total') }}</div>
|
||||
<div class="text-xs text-base-content/50">{{ filteredItems.length }} {{ t('orders.total', 'total') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -34,14 +34,14 @@
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
:placeholder="t('common.search')"
|
||||
class="input input-sm w-full bg-white/10 border-white/20 text-white placeholder:text-white/50"
|
||||
class="input input-sm w-full bg-base-200 border-base-300 text-base-content placeholder:text-base-content/50"
|
||||
/>
|
||||
<Icon name="lucide:search" size="16" class="absolute right-3 top-1/2 -translate-y-1/2 text-white/50" />
|
||||
<Icon name="lucide:search" size="16" class="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/50" />
|
||||
</div>
|
||||
|
||||
<!-- Filter dropdown -->
|
||||
<div class="dropdown dropdown-end w-full">
|
||||
<label tabindex="0" class="btn btn-sm w-full bg-white/10 border-white/20 text-white hover:bg-white/20 justify-between">
|
||||
<label tabindex="0" class="btn btn-sm w-full bg-base-200 border-base-300 text-base-content hover:bg-base-200 justify-between">
|
||||
<span>{{ selectedFilterLabel }}</span>
|
||||
<Icon name="lucide:chevron-down" size="14" />
|
||||
</label>
|
||||
@@ -62,7 +62,7 @@
|
||||
<div
|
||||
v-for="item in displayItems"
|
||||
:key="item.uuid"
|
||||
class="bg-white/10 rounded-lg p-3 hover:bg-white/20 transition-colors cursor-pointer"
|
||||
class="bg-base-200 rounded-lg p-3 hover:bg-base-200 transition-colors cursor-pointer"
|
||||
:class="{ 'ring-2 ring-indigo-500': selectedOrderId === item.uuid }"
|
||||
@click="selectedOrderId = item.uuid"
|
||||
@mouseenter="hoveredOrderId = item.uuid"
|
||||
@@ -74,17 +74,17 @@
|
||||
{{ getStatusText(item.status) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-xs text-white/70 space-y-1">
|
||||
<div class="text-xs text-base-content/70 space-y-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon name="lucide:map-pin" size="12" class="text-white/40" />
|
||||
<Icon name="lucide:map-pin" size="12" class="text-base-content/40" />
|
||||
<span class="truncate">{{ item.sourceLocationName }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon name="lucide:navigation" size="12" class="text-white/40" />
|
||||
<Icon name="lucide:navigation" size="12" class="text-base-content/40" />
|
||||
<span class="truncate">{{ item.destinationLocationName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-white/50 mt-2">
|
||||
<div class="text-xs text-base-content/50 mt-2">
|
||||
{{ getOrderDate(item) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,14 +93,14 @@
|
||||
<div class="text-center py-8">
|
||||
<div class="text-3xl mb-2">📦</div>
|
||||
<div class="font-semibold text-sm mb-1">{{ t('orders.no_orders') }}</div>
|
||||
<div class="text-xs text-white/60">{{ t('orders.no_orders_desc') }}</div>
|
||||
<div class="text-xs text-base-content/60">{{ t('orders.no_orders_desc') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="p-3 border-t border-white/10 flex-shrink-0">
|
||||
<span class="text-xs text-white/50">{{ displayItems.length }} {{ t('catalog.of') }} {{ filteredItems.length }}</span>
|
||||
<div class="p-3 border-t border-base-300 flex-shrink-0">
|
||||
<span class="text-xs text-base-content/50">{{ displayItems.length }} {{ t('catalog.of') }} {{ filteredItems.length }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</CatalogPage>
|
||||
|
||||
Reference in New Issue
Block a user