Redesign offer result card layout
All checks were successful
Build Docker Image / build (push) Successful in 4m53s
All checks were successful
Build Docker Image / build (push) Successful in 4m53s
This commit is contained in:
@@ -1,15 +1,32 @@
|
||||
<template>
|
||||
<Card padding="md" interactive @click="$emit('select')">
|
||||
<!-- Header: Location + Price -->
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div>
|
||||
<Text weight="semibold">{{ supplierDisplay }}</Text>
|
||||
<Text tone="muted" size="sm">
|
||||
{{ t('catalogOfferCard.labels.origin_label') }}: {{ originDisplay }}
|
||||
</Text>
|
||||
<Text v-if="productName" tone="muted" size="sm">{{ productName }}</Text>
|
||||
<!-- Header: Supplier + Price -->
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 rounded-full flex items-center justify-center" style="background-color: #3b82f6">
|
||||
<Icon name="lucide:factory" size="14" class="text-white" />
|
||||
</div>
|
||||
<Text weight="semibold">{{ supplierDisplay }}</Text>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm text-base-content/70">
|
||||
<Icon name="lucide:map-pin" size="14" class="text-base-content/60" />
|
||||
<span>{{ originDisplay }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="productName" class="flex items-center gap-2 text-sm text-base-content/70">
|
||||
<Icon name="lucide:package" size="14" class="text-base-content/60" />
|
||||
<span>{{ productName }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="quantityDisplay" class="flex items-center gap-2 text-sm text-base-content/70">
|
||||
<Icon name="lucide:scale" size="14" class="text-base-content/60" />
|
||||
<span>{{ quantityDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Text v-if="priceDisplay" weight="semibold" class="text-primary text-lg">
|
||||
|
||||
<Text v-if="priceDisplay" weight="semibold" class="text-primary text-lg text-right">
|
||||
{{ priceDisplay }}
|
||||
</Text>
|
||||
</div>
|
||||
@@ -17,13 +34,13 @@
|
||||
<!-- Supplier info -->
|
||||
<SupplierInfoBlock v-if="kycProfileUuid" :kyc-profile-uuid="kycProfileUuid" class="mb-3" />
|
||||
|
||||
<!-- Route stepper -->
|
||||
<RouteStepper
|
||||
v-if="stages.length > 0"
|
||||
:stages="stages"
|
||||
:start-name="startName"
|
||||
:end-name="endName"
|
||||
/>
|
||||
<!-- Route lines -->
|
||||
<div v-if="routeRows.length" class="mt-3 pt-2 border-t border-base-200/60">
|
||||
<div v-for="(row, index) in routeRows" :key="index" class="flex items-center gap-2 text-sm text-base-content/70">
|
||||
<Icon :name="row.icon" size="14" class="text-base-content/60" />
|
||||
<span>{{ row.distanceLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
@@ -35,6 +52,7 @@ const props = withDefaults(defineProps<{
|
||||
supplierName?: string
|
||||
productName?: string
|
||||
pricePerUnit?: number | null
|
||||
quantity?: number | string | null
|
||||
currency?: string | null
|
||||
unit?: string | null
|
||||
stages?: RouteStage[]
|
||||
@@ -60,13 +78,25 @@ const originDisplay = computed(() => {
|
||||
})
|
||||
|
||||
const priceDisplay = computed(() => {
|
||||
if (!props.pricePerUnit) return null
|
||||
if (props.pricePerUnit == null) return null
|
||||
const currSymbol = getCurrencySymbol(props.currency)
|
||||
const unitName = getUnitName(props.unit)
|
||||
const formattedPrice = props.pricePerUnit.toLocaleString()
|
||||
const formattedPrice = Number(props.pricePerUnit).toLocaleString()
|
||||
return `${currSymbol}${formattedPrice}/${unitName}`
|
||||
})
|
||||
|
||||
const quantityDisplay = computed(() => {
|
||||
if (props.quantity == null || props.quantity === '') return null
|
||||
const quantityValue = Number(props.quantity)
|
||||
if (Number.isNaN(quantityValue)) return null
|
||||
const formattedQuantity = quantityValue.toLocaleString()
|
||||
const unitName = getUnitName(props.unit)
|
||||
return t('catalogOfferCard.labels.quantity_with_unit', {
|
||||
quantity: formattedQuantity,
|
||||
unit: unitName
|
||||
})
|
||||
})
|
||||
|
||||
const getCurrencySymbol = (currency?: string | null) => {
|
||||
switch (currency?.toUpperCase()) {
|
||||
case 'USD': return '$'
|
||||
@@ -80,14 +110,44 @@ const getCurrencySymbol = (currency?: string | null) => {
|
||||
const getUnitName = (unit?: string | null) => {
|
||||
switch (unit?.toLowerCase()) {
|
||||
case 'т':
|
||||
case 't':
|
||||
case 'ton':
|
||||
case 'tonne':
|
||||
return 'тонна'
|
||||
return t('catalogOfferCard.labels.default_unit')
|
||||
case 'кг':
|
||||
case 'kg':
|
||||
return 'кг'
|
||||
return t('catalogOfferCard.labels.unit_kg')
|
||||
default:
|
||||
return 'тонна'
|
||||
return t('catalogOfferCard.labels.default_unit')
|
||||
}
|
||||
}
|
||||
|
||||
const formatDistance = (km?: number | null) => {
|
||||
if (km == null) return null
|
||||
const formatted = Math.round(km).toLocaleString()
|
||||
return t('catalogOfferCard.labels.distance_km', { km: formatted })
|
||||
}
|
||||
|
||||
const getTransportIcon = (type?: string | null) => {
|
||||
switch (type) {
|
||||
case 'rail':
|
||||
return 'lucide:train-front'
|
||||
case 'sea':
|
||||
return 'lucide:ship'
|
||||
case 'road':
|
||||
case 'auto':
|
||||
default:
|
||||
return 'lucide:truck'
|
||||
}
|
||||
}
|
||||
|
||||
const routeRows = computed(() =>
|
||||
(props.stages || [])
|
||||
.filter(stage => stage?.distanceKm != null)
|
||||
.map(stage => ({
|
||||
icon: getTransportIcon(stage?.transportType),
|
||||
distanceLabel: formatDistance(stage?.distanceKm)
|
||||
}))
|
||||
.filter(row => !!row.distanceLabel)
|
||||
)
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user