Skip to content

Homepage + Header Design

Date: 2026-02-08 Status: Approved Scope: Homepage implementation + header search/city selector


Overview

Implement the homepage (index.vue) with three content sections (Hero + YMM, Categories, Recent Listings) and enhance the default layout header with search autocomplete and city selector.

API Endpoints Used

EndpointWhere
GET /store/products/search?q=...SearchBar autocomplete
GET /store/categories?type=partCategories grid on homepage
GET /store/products?sort=date_desc&limit=8Recent listings on homepage
GET /store/geo/regions + regions/{id}/citiesCity selector in header
GET /store/cars/makes, /models, /generationsYMM filter (existing)

Reused Components

  • YmmSelect — Hero filter
  • ProductCard — Recent listings
  • useGeoCascade — City selector
  • useGeoStore — Persist selected city

1. Header Enhancement (default layout)

1.1 Search with Autocomplete

Desktop: UInput with search icon between Logo and navigation.

  • Debounce 300ms, minimum 2 characters to trigger request
  • GET /store/products/search?q=... — dropdown with results (title, price, thumbnail)
  • Max 5 results in dropdown
  • Enter with text → /catalog?q=...
  • Click on result → /product/:id
  • Escape or click outside → close dropdown

Mobile: Search icon in header, click expands full-width input.

FSD Architecture:

  • features/search/composables/useProductSearch.ts — debounced API call, returns reactive results
  • features/search/ui/SearchBar.vue — input + dropdown component

1.2 City Selector

  • Button "Санкт-Петербург" with location pin icon (from useGeoStore.locationLabel)
  • Click → UModal with region → city cascade (reuse useGeoCascade)
  • Selected city persists in store + cookie
  • MVP: city is display-only in header, filtering by city happens in catalog

2. Homepage — Hero Section

Content

  • h1: "Найдите запчасть для вашего авто" (i18n key)
  • YMM filter: Make → Model → Generation + "Подобрать" button
    • Reuse features/ymm-select/ui/YmmSelect.vue
    • Submit → navigateTo('/catalog', { query: { make_id, model_id, generation_id } })
    • Desktop: horizontal layout (3 selects + button in row)
    • Mobile: vertical stack

Visual

  • Background: subtle gradient or bg-[var(--ui-bg-elevated)]
  • Centered content, max-w-3xl
  • Padding: py-16 desktop, py-10 mobile
  • Dark mode via semantic CSS variables

Architecture

  • Inline in index.vue — no separate widget needed
  • YMM component via FSD auto-import

3. Homepage — Categories Section

Data

  • GET /store/categories?type=part via useAsyncData (SSR)
  • Show only root categories (parent_id === null)
  • Schema: categorySchemaid, name, slug, icon

Visual

  • Section title: "Категории запчастей"
  • Grid: desktop 3-5 columns, mobile 2 columns
  • Card: icon (from icon field, fallback default) + name
  • Click → /catalog?category_id=X
  • Hover effect, rounded corners, subtle border
  • Dark mode: bg-[var(--ui-bg-elevated)] + border-[var(--ui-border)]

Edge Cases

  • 0 categories → hide section
  • API error → hide section (don't block page)

4. Homepage — Recent Listings Section

Data

  • GET /store/products?sort=date_desc&limit=8 via useAsyncData (SSR)
  • Response type: ApiListResponse<Product>

Visual

  • Title: "Новые объявления" + "Смотреть все" link → /catalog
  • Grid: desktop 4 columns, tablet 3, mobile 2
  • Reuse entities/product/ui/ProductCard.vue
  • Bottom: "Показать ещё" button → /catalog (no infinite scroll on homepage)

States

  • Loading: 4 skeleton cards (desktop), 2 (mobile) via USkeleton
  • Empty: "Объявлений пока нет" + "Разместить первое" CTA
  • Error: hide section silently

5. Files to Create/Modify

FileActionDescription
features/search/composables/useProductSearch.tsCreateDebounced search via /store/products/search
features/search/ui/SearchBar.vueCreateInput + autocomplete dropdown
app/layouts/default.vueModifyAdd SearchBar + city selector
app/pages/index.vueModifyHero + YMM + categories + listings
i18n/locales/ru.jsonModifyAdd homepage i18n keys

6. i18n Keys

json
{
  "home": {
    "hero": {
      "title": "Найдите запчасть для вашего авто",
      "submit": "Подобрать"
    },
    "categories": {
      "title": "Категории запчастей"
    },
    "listings": {
      "title": "Новые объявления",
      "viewAll": "Смотреть все",
      "showMore": "Показать ещё",
      "empty": "Объявлений пока нет",
      "emptyAction": "Разместить первое"
    }
  },
  "search": {
    "placeholder": "Поиск запчастей...",
    "noResults": "Ничего не найдено",
    "submit": "Найти"
  }
}