fix(auth): org-scoped team tokens and header search order

This commit is contained in:
Ruslan Bakiev
2026-04-21 14:09:51 +07:00
parent e1e6993f35
commit 39712613ae
8 changed files with 157 additions and 76 deletions

View File

@@ -43,16 +43,36 @@ const LANDING_SEARCH_TOP_STOP = 16
const LANDING_SEARCH_BOTTOM_GAP = 30
const logisticsSearch = reactive({
from: '',
to: '',
cargo: '',
destination: '',
product: '',
quantity: '',
})
function syncSearchFromRoute() {
hydrateFromQuery(route.query)
logisticsSearch.from = typeof route.query.from === 'string' ? route.query.from : isCalcPage.value ? calcDraft.value.from : ''
logisticsSearch.to = typeof route.query.to === 'string' ? route.query.to : isCalcPage.value ? calcDraft.value.to : ''
logisticsSearch.cargo = typeof route.query.cargo === 'string' ? route.query.cargo : isCalcPage.value ? calcDraft.value.cargo : ''
logisticsSearch.destination = typeof route.query.hubName === 'string'
? route.query.hubName
: typeof route.query.to === 'string'
? route.query.to
: isCalcPage.value
? calcDraft.value.to
: ''
logisticsSearch.product = typeof route.query.productName === 'string'
? route.query.productName
: typeof route.query.from === 'string'
? route.query.from
: isCalcPage.value
? calcDraft.value.from
: ''
logisticsSearch.quantity = typeof route.query.qty === 'string'
? route.query.qty
: typeof route.query.quantity === 'string'
? route.query.quantity
: typeof route.query.cargo === 'string'
? route.query.cargo
: isCalcPage.value
? calcDraft.value.cargo
: ''
}
function inferCountryIso(value: string, fallback: string) {
@@ -69,10 +89,8 @@ function isoToFlag(iso: string) {
return String.fromCodePoint(...[...normalized].map(char => 127397 + char.charCodeAt(0)))
}
const fromIso = computed(() => inferCountryIso(logisticsSearch.from, 'CN'))
const toIso = computed(() => inferCountryIso(logisticsSearch.to, 'RU'))
const fromFlag = computed(() => isoToFlag(fromIso.value))
const toFlag = computed(() => isoToFlag(toIso.value))
const destinationIso = computed(() => inferCountryIso(logisticsSearch.destination, 'RU'))
const destinationFlag = computed(() => isoToFlag(destinationIso.value))
const showAdminDock = computed(() => Boolean(showLogistics.value) && !isAuthPage.value)
// Fullscreen menu
const isMenuOpen = ref(false)
@@ -212,53 +230,60 @@ const headerBackdropClass = computed(() => {
return 'header-glass-backdrop--default'
})
function buildHeaderSearchQuery(from: string, to: string, cargo: string) {
function buildHeaderSearchQuery(destination: string, product: string, quantity: string) {
const currentQuery = route.query || {}
const patch = {
from,
to,
cargo,
from: product,
to: destination,
cargo: quantity,
}
const semanticQuery = {
...(product ? { productName: product } : {}),
...(destination ? { hubName: destination } : {}),
...(quantity ? { qty: quantity, quantity } : {}),
}
if (isCalcPage.value) {
return {
...currentQuery,
...buildCalcQuery(patch),
...semanticQuery,
}
}
return {
...currentQuery,
...(from ? { from } : {}),
...(to ? { to } : {}),
...(cargo ? { cargo } : {}),
...(product ? { from: product } : {}),
...(destination ? { to: destination } : {}),
...(quantity ? { cargo: quantity } : {}),
...semanticQuery,
}
}
async function submitHeaderSearch() {
const from = logisticsSearch.from.trim()
const to = logisticsSearch.to.trim()
const cargo = logisticsSearch.cargo.trim()
const destination = logisticsSearch.destination.trim()
const product = logisticsSearch.product.trim()
const quantity = logisticsSearch.quantity.trim()
await navigateToLocalized({
path: '/catalog',
query: buildHeaderSearchQuery(from, to, cargo),
query: buildHeaderSearchQuery(destination, product, quantity),
})
}
type StepRoute = 'from' | 'to' | 'cargo'
type StepRoute = 'destination' | 'product' | 'quantity'
async function openStep(step: StepRoute) {
const from = logisticsSearch.from.trim()
const to = logisticsSearch.to.trim()
const cargo = logisticsSearch.cargo.trim()
const stepPath = step === 'from'
? '/catalog/product'
: step === 'to'
? '/catalog/destination'
const destination = logisticsSearch.destination.trim()
const product = logisticsSearch.product.trim()
const quantity = logisticsSearch.quantity.trim()
const stepPath = step === 'destination'
? '/catalog/destination'
: step === 'product'
? '/catalog/product'
: '/catalog/quantity'
await navigateToLocalized({
path: stepPath,
query: buildHeaderSearchQuery(from, to, cargo),
query: buildHeaderSearchQuery(destination, product, quantity),
})
}
@@ -297,7 +322,7 @@ async function goToSignIn() {
<button
type="button"
class="btn btn-secondary h-10 min-h-0 w-full rounded-full text-sm font-semibold"
@click="openStep('from')"
@click="openStep('destination')"
>
{{ $t('ui.calculate') }}
</button>
@@ -321,43 +346,46 @@ async function goToSignIn() {
<div :class="searchCapsuleClass">
<form class="flex min-w-0 flex-wrap items-center gap-2 rounded-full" @submit.prevent="submitHeaderSearch">
<label class="search-arch input flex h-11 min-h-0 min-w-[170px] flex-1 items-center gap-3 rounded-full shadow-none">
<span class="shrink-0 text-base leading-none">{{ fromFlag }}</span>
<span class="shrink-0 text-base leading-none">{{ destinationFlag }}</span>
<input
:value="logisticsSearch.from"
type="text"
class="w-full cursor-pointer"
:placeholder="$t('ui.from')"
readonly
@focus.prevent="openStep('from')"
@click.prevent="openStep('from')"
/>
</label>
<label class="search-arch input flex h-11 min-h-0 min-w-[170px] flex-1 items-center gap-3 rounded-full shadow-none">
<span class="shrink-0 text-base leading-none">{{ toFlag }}</span>
<input
:value="logisticsSearch.to"
:value="logisticsSearch.destination"
type="text"
class="w-full cursor-pointer"
:placeholder="$t('ui.to')"
readonly
@focus.prevent="openStep('to')"
@click.prevent="openStep('to')"
@focus.prevent="openStep('destination')"
@click.prevent="openStep('destination')"
/>
</label>
<label class="search-arch input flex h-11 min-h-0 min-w-[190px] flex-[1.4] items-center gap-3 rounded-full shadow-none">
<label class="search-arch input flex h-11 min-h-0 min-w-[170px] flex-1 items-center gap-3 rounded-full shadow-none">
<svg viewBox="0 0 24 24" fill="none" class="h-4 w-4 shrink-0 text-base-content/60" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
<path d="m3.3 7 8.7 5 8.7-5" />
<path d="M12 22V12" />
</svg>
<input
:value="logisticsSearch.cargo"
:value="logisticsSearch.product"
type="text"
class="w-full cursor-pointer"
:placeholder="$t('ui.cargo')"
:placeholder="$t('ui.product')"
readonly
@focus.prevent="openStep('cargo')"
@click.prevent="openStep('cargo')"
@focus.prevent="openStep('product')"
@click.prevent="openStep('product')"
/>
</label>
<label class="search-arch input flex h-11 min-h-0 min-w-[170px] flex-1 items-center gap-3 rounded-full shadow-none">
<svg viewBox="0 0 24 24" fill="none" class="h-4 w-4 shrink-0 text-base-content/60" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M10 13a2 2 0 1 1 0-4h4a2 2 0 1 1 0 4h-4Zm0 0v2m4-2v2" />
<path d="M6 5h12M6 19h12" />
</svg>
<input
:value="logisticsSearch.quantity"
type="text"
class="w-full cursor-pointer"
:placeholder="$t('ui.quantity')"
readonly
@focus.prevent="openStep('quantity')"
@click.prevent="openStep('quantity')"
/>
</label>
<button type="submit" class="btn btn-secondary h-11 min-h-0 rounded-full px-5">{{ $t('ui.find') }}</button>