feat(catalog): implement step-by-step navigation for offers and suppliers
All checks were successful
Build Docker Image / build (push) Successful in 4m49s
All checks were successful
Build Docker Image / build (push) Successful in 4m49s
- Transform offers/index.vue to show products list with sparkline charts - Create nested routes for offers: /offers → /offers/[productId] → /offers/[productId]/[hubId] - Create nested routes for suppliers: /suppliers → /suppliers/[supplierId] → /suppliers/[supplierId]/[productId] → /suppliers/[supplierId]/[productId]/[hubId] - Add OffersBreadcrumbs and SuppliersBreadcrumbs components for navigation - Update HubCard to accept custom linkTo prop - Key difference: Suppliers calculation uses FindRoutes (single source), Offers uses FindProductRoutes (all sources)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<component
|
||||
:is="linkable ? NuxtLink : 'div'"
|
||||
:to="linkable ? localePath(`/catalog/hubs/${hub.uuid}`) : undefined"
|
||||
:to="linkable ? resolvedLink : undefined"
|
||||
class="block"
|
||||
:class="{ 'cursor-pointer': selectable }"
|
||||
@click="selectable && $emit('select')"
|
||||
@@ -47,6 +47,7 @@ const props = defineProps<{
|
||||
hub: Hub
|
||||
selectable?: boolean
|
||||
isSelected?: boolean
|
||||
linkTo?: string
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
@@ -57,7 +58,8 @@ defineEmits<{
|
||||
const localePath = useLocalePath()
|
||||
const { t } = useI18n()
|
||||
|
||||
const linkable = computed(() => !props.selectable && props.hub.uuid)
|
||||
const linkable = computed(() => !props.selectable && (props.linkTo || props.hub.uuid))
|
||||
const resolvedLink = computed(() => props.linkTo || localePath(`/catalog/hubs/${props.hub.uuid}`))
|
||||
|
||||
// ISO code to emoji flag
|
||||
const isoToEmoji = (code: string): string => {
|
||||
|
||||
51
app/components/catalog/OffersBreadcrumbs.vue
Normal file
51
app/components/catalog/OffersBreadcrumbs.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="breadcrumbs text-sm">
|
||||
<ul>
|
||||
<li v-for="(crumb, index) in breadcrumbs" :key="index">
|
||||
<NuxtLink v-if="crumb.to" :to="crumb.to" class="hover:text-primary">
|
||||
{{ crumb.label }}
|
||||
</NuxtLink>
|
||||
<span v-else class="text-base-content">{{ crumb.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
productId?: string
|
||||
productName?: string
|
||||
hubId?: string
|
||||
hubName?: string
|
||||
}>()
|
||||
|
||||
const localePath = useLocalePath()
|
||||
const { t } = useI18n()
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
const crumbs: Array<{ label: string; to?: string }> = []
|
||||
|
||||
// Products list
|
||||
crumbs.push({
|
||||
label: t('breadcrumbs.products', 'Products'),
|
||||
to: localePath('/catalog/offers')
|
||||
})
|
||||
|
||||
// Product
|
||||
if (props.productId) {
|
||||
crumbs.push({
|
||||
label: props.productName || `#${props.productId.slice(0, 8)}...`,
|
||||
to: props.hubId ? localePath(`/catalog/offers/${props.productId}`) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
// Hub
|
||||
if (props.hubId) {
|
||||
crumbs.push({
|
||||
label: props.hubName || `#${props.hubId.slice(0, 8)}...`
|
||||
})
|
||||
}
|
||||
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
63
app/components/catalog/SuppliersBreadcrumbs.vue
Normal file
63
app/components/catalog/SuppliersBreadcrumbs.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="breadcrumbs text-sm">
|
||||
<ul>
|
||||
<li v-for="(crumb, index) in breadcrumbs" :key="index">
|
||||
<NuxtLink v-if="crumb.to" :to="crumb.to" class="hover:text-primary">
|
||||
{{ crumb.label }}
|
||||
</NuxtLink>
|
||||
<span v-else class="text-base-content">{{ crumb.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
supplierId?: string
|
||||
supplierName?: string
|
||||
productId?: string
|
||||
productName?: string
|
||||
hubId?: string
|
||||
hubName?: string
|
||||
}>()
|
||||
|
||||
const localePath = useLocalePath()
|
||||
const { t } = useI18n()
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
const crumbs: Array<{ label: string; to?: string }> = []
|
||||
|
||||
// Suppliers list
|
||||
crumbs.push({
|
||||
label: t('breadcrumbs.suppliers', 'Suppliers'),
|
||||
to: localePath('/catalog/suppliers')
|
||||
})
|
||||
|
||||
// Supplier
|
||||
if (props.supplierId) {
|
||||
const hasNext = props.productId
|
||||
crumbs.push({
|
||||
label: props.supplierName || `#${props.supplierId.slice(0, 8)}...`,
|
||||
to: hasNext ? localePath(`/catalog/suppliers/${props.supplierId}`) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
// Product
|
||||
if (props.productId) {
|
||||
const hasNext = props.hubId
|
||||
crumbs.push({
|
||||
label: props.productName || `#${props.productId.slice(0, 8)}...`,
|
||||
to: hasNext ? localePath(`/catalog/suppliers/${props.supplierId}/${props.productId}`) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
// Hub
|
||||
if (props.hubId) {
|
||||
crumbs.push({
|
||||
label: props.hubName || `#${props.hubId.slice(0, 8)}...`
|
||||
})
|
||||
}
|
||||
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user