Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: breadcrumbs #188

Merged
merged 3 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/food/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ export default defineAppConfig({
delivery: 'lucide:truck',
info: 'lucide:info',
bike: 'lucide:bike',
undo: 'lucide:undo-2',
basket: 'lucide:shopping-basket',
},
})
35 changes: 35 additions & 0 deletions apps/food/app/components/Breadcrumbs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<div class="mb-6 flex flex-row justify-between items-center">
<nav class="hidden lg:block">
<ol class="flex flex-row flex-wrap gap-y-2 items-center">
<li v-for="link in links" :key="link.title" class="relative max-w-[20rem] after:content-['/'] after:px-1 after:text-lg after:text-gray-300 last:after:content-['']">
<NuxtLink
:to="link.href"
class="px-3 py-2 inline-block leading-tight rounded-xl active:scale-95 lg:hover:bg-zinc-200 lg:hover:scale-95 lg:active:scale-90 duration-200 bg-gray-50 data-[active=true]:bg-gray-50"
:data-active="link.href === '#'"
>
{{ link.title }}
</NuxtLink>
</li>
</ol>
</nav>

<div class="w-full lg:w-auto mx-auto md:mx-0">
<button
class="px-5 py-3 w-full flex flex-row gap-2 justify-center items-center text-base font-normal cursor-pointer rounded-2xl bg-gray-200 active:scale-95 hover:bg-gray-300 lg:hover:scale-95 lg:active:scale-90 duration-200"
@click="router.back()"
>
<Icon :name="icons.undo" size="20" /> Вернуться
</button>
</div>
</div>
</template>

<script setup lang="ts">
const { breadcrumbs: links } = defineProps<{
breadcrumbs: { title: string, href: string }[]
}>()

const { icons } = useAppConfig()
const router = useRouter()
</script>
8 changes: 4 additions & 4 deletions apps/food/app/components/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
<button
aria-label="Close Navigation"
:data-active="isNavbarOpened"
class="hidden data-[active=true]:block"
class="hidden data-[active=true]:flex items-center"
@click="isNavbarOpened = !isNavbarOpened"
>
<Icon :name="icons.close" class="w-8 h-8" />
<Icon :name="icons.close" class="w-10 h-10" />
</button>
<button
aria-label="Open Navigation"
:data-active="!isNavbarOpened"
class="hidden data-[active=true]:block"
class="hidden data-[active=true]:flex items-center"
@click="isNavbarOpened = !isNavbarOpened"
>
<Icon :name="icons.menu" class="w-8 h-8" />
<Icon :name="icons.menu" class="w-10 h-10" />
</button>
</div>

Expand Down
8 changes: 2 additions & 6 deletions apps/food/app/components/Navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
</div>
</div>

<div class="mb-8">
<div v-if="checkout" class="mb-8">
<DeliveryInfoBlock />

<button
v-if="!!checkout"
class="flex flex-row gap-2 items-center active:scale-95 lg:hover:scale-95 lg:active:scale-90 duration-200"
@click="isDeliveryInfoModalOpened = !isDeliveryInfoModalOpened"
>
Expand Down Expand Up @@ -50,8 +49,5 @@
const { isNavbarOpened, isDeliveryInfoModalOpened } = useApp()
const { icons } = useAppConfig()
const channelData = await useChannel()

const checkout = {
deliveryMethod: 'DELIVERY',
}
const { checkout } = await useCheckout()
</script>
2 changes: 1 addition & 1 deletion apps/food/app/components/cart/Cart.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="relative bg-white rounded-2xl px-4 py-4 h-full flex flex-col justify-between">
<div v-if="checkout" class="relative bg-white rounded-2xl px-4 py-4 h-full flex flex-col justify-between">
<div class="h-screen overflow-y-auto">
<div class="mb-48">
<div class="mb-4 flex flex-row justify-between items-center">
Expand Down
116 changes: 109 additions & 7 deletions apps/food/app/pages/catalog/[categorySlug]/[productSlug]/index.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,120 @@
<template>
{{ slug }}
{{ product }}
<Breadcrumbs :breadcrumbs="breadcrumbs" />

<div v-for="variant in product?.variants" :key="variant.id">
<button @click="addProduct(variant.id)">
Добавить в корзину
</button>
<div class="bg-white px-5 py-5 rounded-2xl">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-x-0 gap-y-4 sm:gap-4">
<div class="col-span-1 relative w-full aspect-square">
<img
:src="productImageUrl"
alt=""
priority
fill
sizes="(min-width: 1024px) 20vw, (min-width: 768px) 25vw, (min-width: 640px) 33vw, (min-width: 475px) 50vw, 100vw"
class="rounded-xl object-cover object-center"
>
</div>

<div class="col-span-2">
<h1 class="text-xl md:text-2xl lg:text-3xl font-medium">
{{ product?.name }}
</h1>
<div class="mt-1 font-normal text-gray-400">
{{ productVariant?.weightValue }}
{{ getWeightLocalizedUnit('G') }}
</div>

<div class="mt-4 flex flex-row gap-6 items-center">
<div class="text-2xl font-medium tracking-tight">
{{ getLocalizedPrice(productVariant?.gross) }} <span class="text-xl">₽</span>
</div>

<button
class="button-gradient px-5 py-3 flex flex-row gap-2 text-base font-normal cursor-pointer rounded-2xl active:scale-95 lg:hover:scale-95 lg:active:scale-90 duration-200"
@click="addProduct(productVariant?.id ?? '')"
>
<Icon :name="icons.basket" size="24" /> Добавить в корзину
</button>
</div>

<div v-if="inCart" class="mt-2 text-gray-500">
В корзине!
</div>
</div>
</div>

<div class="mt-6 flex flex-col xl:flex-row justify-between gap-4">
<div v-if="product?.description">
<div class="mb-1 font-medium text-gray-400">
Описание
</div>
<div class="leading-snug">
{{ product.description }}
</div>
</div>

<div v-if="isNutritionShown">
<div class="mb-1 font-medium text-gray-400">
На 100 грамм
</div>
<div class="mt-2 px-4 py-4 w-fit flex flex-row gap-4 bg-gray-100 rounded-2xl">
<div>
<div class="font-medium">
{{ per100gEnergy }}
</div>
<div>ккал</div>
</div>
<div>
<div class="font-medium">
{{ per100gProtein }} г
</div>
<div>белки</div>
</div>
<div>
<div class="font-medium">
{{ per100gFat }} г
</div>
<div>жиры</div>
</div>
<div>
<div class="font-medium">
{{ per100gCarbohydrate }} г
</div>
<div>углеводы</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
const route = useRoute()
const slug = route.params.productSlug

const { addProduct } = await useCheckout()
const { icons } = useAppConfig()
const { addProduct, checkout } = await useCheckout()
const { data: product } = await useFetch(`/api/product/slug/${slug}`)
const productVariant = computed(() => product.value?.variants[0])

const inCart = computed(() => {
const line = checkout.value?.lines?.find((l) => l.variant.id === productVariant.value?.id)
return line
})

const breadcrumbs = [
{ title: 'Главная', href: '/' },
{
title: product.value?.category?.name ?? '',
href: `/catalog/${product.value?.category?.slug}`,
},
{ title: product.value?.name ?? '', href: '#' },
]

const productImageUrl = '/burger-1.jpg'

const per100gEnergy = 612
const per100gProtein = 21.9
const per100gCarbohydrate = 45.8
const per100gFat = 29.6
const isNutritionShown = true
</script>
32 changes: 29 additions & 3 deletions apps/food/app/pages/catalog/[categorySlug]/index.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
<template>
{{ slug }}
<Breadcrumbs :breadcrumbs="breadcrumbs" />

<h1 class="text-3xl font-medium">
{{ category?.name }}
</h1>
<div>Здесь представлены все товары из этой категории</div>

<div class="mt-4 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4 gap-2">
<ProductCard v-for="product in category?.products" :key="product.id" :product-id="product.id" />
</div>
</template>

<script setup lang="ts">
const route = useRoute()
const slug = route.params.categorySlug
definePageMeta({
validate: async ({ params }) => {
const { error } = await useFetch(`/api/category/slug/${params.categorySlug}`)
return error.value === undefined
},
})

const { params } = useRoute()
const slug = params.categorySlug

const { data: category } = await useFetch(`/api/category/slug/${slug}`)

const breadcrumbs = [
{ title: 'Главная', href: '/' },
{
title: category.value?.name ?? '',
href: '#',
},
]
</script>
35 changes: 35 additions & 0 deletions apps/food/server/api/category/slug/[slug].get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export default defineEventHandler(async (event) => {
const { channelId } = useRuntimeConfig()
const slug = getRouterParam(event, 'slug')

const activeMenu = await prisma.menu.findFirst({
where: { channelId },
include: {
categories: {
where: { slug },
include: {
products: {
include: {
variants: true,
},
},
},
},
},
})
if (!activeMenu) {
throw createError({
statusCode: 404,
statusMessage: 'No menu',
})
}

if (!activeMenu.categories[0]) {
throw createError({
statusCode: 404,
statusMessage: 'Not found',
})
}

return activeMenu.categories[0]
})
1 change: 1 addition & 0 deletions apps/food/server/api/product/slug/[slug].get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default defineEventHandler(async (event) => {
where: { slug, channelId },
include: {
variants: true,
category: true,
},
})
})
Loading