feat: step-by-step quote flow like logistics project
All checks were successful
Build Docker Image / build (push) Successful in 5m11s

New pages: /catalog/product → /catalog/destination → /catalog/quantity → /catalog/results
Each step has fullscreen map + white bottom sheet card (rounded-t-3xl).
Header capsule in quote mode now navigates between steps.
i18n keys added for step titles (en/ru).
This commit is contained in:
Ruslan Bakiev
2026-03-10 11:52:35 +07:00
parent 055d682167
commit 61a37040d6
7 changed files with 775 additions and 41 deletions

View File

@@ -42,15 +42,15 @@
>
{{ $t('catalog.modes.explore') }}
</button>
<button
<NuxtLink
:to="localePath('/catalog/product')"
class="px-3 py-1 text-sm font-medium rounded-full transition-colors"
:class="showActiveMode && catalogMode === 'quote' && !isClientArea
:class="isQuoteStepPage
? (useWhiteText ? 'bg-white/20 text-white' : 'bg-base-300 text-base-content')
: (useWhiteText ? 'text-white/70 hover:text-white hover:bg-white/10' : 'text-base-content/70 hover:text-base-content hover:bg-base-200')"
@click="$emit('set-catalog-mode', 'quote')"
>
{{ $t('catalog.modes.quote') }}
</button>
</NuxtLink>
<!-- Role switcher: Я клиент + dropdown -->
<div v-if="loggedIn" class="flex items-center">
<NuxtLink
@@ -139,49 +139,39 @@
</div>
</template>
<!-- Quote mode: Simple segmented input with search inside (white glass) -->
<!-- Quote mode: Step-based capsule navigation (like logistics) -->
<template v-else-if="catalogMode === 'quote'">
<div class="flex items-center w-full rounded-full pill-glass overflow-hidden">
<!-- Product segment -->
<button
class="flex-1 px-4 py-2 text-left hover:bg-base-200/50 rounded-l-full transition-colors min-w-0"
@click="$emit('edit-token', 'product')"
<NuxtLink
:to="localePath('/catalog/product')"
class="flex-1 px-4 py-2 text-left hover:bg-white/10 rounded-l-full transition-colors min-w-0"
>
<div class="text-xs text-base-content/60">{{ $t('catalog.filters.product') }}</div>
<span class="text-[10px] font-bold uppercase tracking-wider opacity-60">{{ $t('catalog.filters.product') }}</span>
<div class="font-medium truncate text-base-content">{{ productLabel || $t('catalog.quote.selectProduct') }}</div>
</button>
<div class="w-px h-8 bg-white/20 self-center" />
</NuxtLink>
<div class="w-px h-8 bg-base-300/40 self-center" />
<!-- Hub segment -->
<button
class="flex-1 px-4 py-2 text-left hover:bg-base-200/50 transition-colors min-w-0"
@click="$emit('edit-token', 'hub')"
<NuxtLink
:to="localePath('/catalog/destination')"
class="flex-1 px-4 py-2 text-left hover:bg-white/10 transition-colors min-w-0"
>
<div class="text-xs text-base-content/60">{{ $t('catalog.filters.hub') }}</div>
<span class="text-[10px] font-bold uppercase tracking-wider opacity-60">{{ $t('catalog.filters.hub') }}</span>
<div class="font-medium truncate text-base-content">{{ hubLabel || $t('catalog.quote.selectHub') }}</div>
</button>
<div class="w-px h-8 bg-white/20 self-center" />
<!-- Quantity segment (inline input) -->
<div class="flex-1 px-4 py-2 min-w-0">
<div class="text-xs text-base-content/60">{{ $t('catalog.filters.quantity') }}</div>
<div class="flex items-center gap-1">
<input
v-model="localQuantity"
type="number"
min="0"
step="0.1"
placeholder="—"
class="w-16 font-medium bg-transparent outline-none text-base-content [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
@blur="$emit('update-quantity', localQuantity)"
@keyup.enter="$emit('update-quantity', localQuantity)"
/>
<span v-if="localQuantity" class="text-base-content/60 text-sm">{{ $t('units.t') }}</span>
</div>
</div>
<!-- Search button inside -->
</NuxtLink>
<div class="w-px h-8 bg-base-300/40 self-center" />
<!-- Quantity segment -->
<NuxtLink
:to="localePath('/catalog/quantity')"
class="flex-1 px-4 py-2 text-left hover:bg-white/10 transition-colors min-w-0"
>
<span class="text-[10px] font-bold uppercase tracking-wider opacity-60">{{ $t('catalog.filters.quantity') }}</span>
<div class="font-medium truncate text-base-content">{{ quantity || '—' }} {{ quantity ? $t('units.t') : '' }}</div>
</NuxtLink>
<!-- Search button -->
<button
class="btn btn-primary btn-circle m-1"
:disabled="!canSearch"
@click="$emit('search')"
@click="navigateToSearch"
>
<Icon name="lucide:search" size="18" />
</button>
@@ -449,6 +439,22 @@ const switchLocalePath = useSwitchLocalePath()
const { t } = useI18n()
const { chatOpen } = toRefs(props)
const router = useRouter()
// Check if we're on a quote step page
const isQuoteStepPage = computed(() => {
const path = route.path
return path.includes('/catalog/product') ||
path.includes('/catalog/destination') ||
path.includes('/catalog/quantity') ||
path.includes('/catalog/results')
})
// Navigate to search results (quote mode step flow)
const navigateToSearch = () => {
router.push(localePath('/catalog/product'))
}
// Check if client area tab is active
const isClientAreaTabActive = (path: string) => {
const currentPath = route.path