Unify offer cards: RouteStepper + OfferResultCard components
All checks were successful
Build Docker Image / build (push) Successful in 4m36s
All checks were successful
Build Docker Image / build (push) Successful in 4m36s
- Add RouteStepper component with transport icons (🚛 🚂 🚢) - Add OfferResultCard with price, distance, route stages - Update hub page to use OfferResultCard - Update CalcResultContent to use OfferResultCard
This commit is contained in:
52
app/components/catalog/OfferResultCard.vue
Normal file
52
app/components/catalog/OfferResultCard.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<Card padding="md" interactive @click="$emit('select')">
|
||||
<!-- Header: Source + Price -->
|
||||
<div class="flex items-start justify-between mb-2">
|
||||
<div>
|
||||
<Text weight="semibold">{{ sourceName }}</Text>
|
||||
<Text v-if="productName" tone="muted" size="sm">{{ productName }}</Text>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<Text v-if="priceDisplay" weight="semibold" class="text-primary text-lg">
|
||||
{{ priceDisplay }}
|
||||
</Text>
|
||||
<Text tone="muted" size="sm">{{ formatDistance(totalDistance) }} км</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Route stepper -->
|
||||
<RouteStepper v-if="stages.length > 0" :stages="stages" />
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteStage } from './RouteStepper.vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
sourceName: string
|
||||
productName?: string
|
||||
pricePerUnit?: number | null
|
||||
currency?: string | null
|
||||
unit?: string | null
|
||||
totalDistance: number
|
||||
stages?: RouteStage[]
|
||||
}>(), {
|
||||
stages: () => []
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
select: []
|
||||
}>()
|
||||
|
||||
const priceDisplay = computed(() => {
|
||||
if (!props.pricePerUnit) return null
|
||||
const curr = props.currency || 'USD'
|
||||
const u = props.unit || 'т'
|
||||
return `${props.pricePerUnit} ${curr}/${u}`
|
||||
})
|
||||
|
||||
const formatDistance = (km?: number | null) => {
|
||||
if (!km) return '0'
|
||||
return Math.round(km).toLocaleString()
|
||||
}
|
||||
</script>
|
||||
39
app/components/catalog/RouteStepper.vue
Normal file
39
app/components/catalog/RouteStepper.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-1 flex-wrap text-xs">
|
||||
<template v-for="(stage, index) in stages" :key="index">
|
||||
<div v-if="index > 0" class="w-3 h-px bg-base-300" />
|
||||
<div class="flex items-center gap-0.5">
|
||||
<span>{{ getTransportIcon(stage.transportType) }}</span>
|
||||
<span class="text-base-content/70">{{ formatDistance(stage.distanceKm) }}км</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
export interface RouteStage {
|
||||
transportType?: string | null
|
||||
distanceKm?: number | null
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
stages: RouteStage[]
|
||||
}>()
|
||||
|
||||
const getTransportIcon = (type?: string | null) => {
|
||||
switch (type) {
|
||||
case 'rail':
|
||||
return '🚂'
|
||||
case 'sea':
|
||||
return '🚢'
|
||||
case 'road':
|
||||
default:
|
||||
return '🚛'
|
||||
}
|
||||
}
|
||||
|
||||
const formatDistance = (km?: number | null) => {
|
||||
if (!km) return '0'
|
||||
return Math.round(km).toLocaleString()
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user