Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProcesosAgilesUMSS/sansistore/llms.txt

Use this file to discover all available pages before exploring further.

The SansiStore product catalog is the primary storefront, available at /productos. It loads all active products from the products Firestore collection and merges each product’s live inventory data from the inventory collection in a single parallel fetch. Buyers can filter by category, toggle an offers-only view, sort results, run full-text search with history, and navigate directly from a product card to the full detail page at /productos/[slug].

Buyer Journey

1

Browse the catalog

Visit /productos. The <FeaturedProducts> component fetches active products via fetchCatalogProducts(), which queries products (where active == true, ordered by createdAt desc) and joins each record with its inventory doc.
2

Filter and sort

Use the <CategoryFilter> dropdown to narrow by category. Toggle Ofertas to show only discounted items. Use the sort dropdown (Popular · Recientes · A-Z · Z-A) to reorder results. All filter state is reflected in URL query params (?category=, ?offers=true, ?sort=, ?page=).
3

Search

Type in the search bar. Up to five matching product suggestions appear in a live dropdown alongside up to five recent search history entries (stored in localStorage under sansistore-search-history). Press Enter or click a suggestion to apply the search; matching text is highlighted inline.
4

View product detail

Click any product card to open /productos/[slug]. The detail page shows the full product image, price (or offer price), stock status, add-to-cart, add-to-favorites, breadcrumb navigation, and a reviews section.
5

Add to cart or save to favorites

Tap Agregar al carrito to add the item immediately. Tap the heart icon to toggle the product in or out of the favorites list. Both actions work for guest and authenticated users.

The <FeaturedProducts> Component

FeaturedProducts (src/components/FeaturedProducts.tsx) is a client-side React 19 component wrapped in a <CartProvider>. It accepts the following props, all of which can be seeded from Astro’s URL search-params at the page level:
interface FeaturedProductsProps {
  initialSearch?: string;       // ?q=
  initialCategory?: string | null; // ?category=
  initialOffersOnly?: boolean;  // ?offers=true
  initialSort?: string | null;  // ?sort= (best-sellers | recent | name-asc | name-desc)
  initialPage?: number;         // ?page=
  favoritesOnly?: boolean;      // true on /favoritos
  title?: string;               // heading text
}
The /productos Astro page seeds these props from Astro.url.searchParams:
// src/pages/productos/index.astro
<FeaturedProducts
  client:load
  initialSearch={initialSearch}
  initialCategory={initialCategory}
  initialOffersOnly={initialOffersOnly}
  initialSort={initialSort}
  initialPage={Number.isNaN(initialPage) ? 1 : initialPage}
/>
Results are paginated at 12 items per page. Sort options available:
ValueLabel
best-sellersPopular
recentRecientes
name-ascA-Z
name-descZ-A

Category Filtering

<CategoryFilter> (src/components/CategoryFilter.tsx) is a self-contained dropdown that queries the categories Firestore collection ordered by name asc and filters out any category where active === false. Selecting a category sets the ?category=<id> URL param and notifies the parent via onCategoryChange.
interface CategoryFilterProps {
  selectedCategory: string | null;
  onCategoryChange: (categoryId: string | null) => void;
}
The dropdown includes an inline search field that filters visible categories client-side without a network round-trip. Selecting Todas clears the active filter.
The catalog validates the ?category= param on mount by checking whether the Firestore document exists and has active !== false. Invalid category IDs are silently removed from the URL.

Product Cards

Each <ProductCard> (src/features/catalog/components/ProductCard.tsx) renders one product from the merged catalog + inventory result. The card shows:

Name & Price

Product name (up to two lines, truncated), current price formatted as Bs X.XX. When hasOffer is true, the offer price is shown prominently and the original price appears with a strikethrough.

Badges

Offer/discount badges appear in red (bg-red-600). Custom badge text appears in the brand primary color. A separate Popular amber badge is applied when soldCount meets the popularity threshold from isPopularProduct().

Stock Status

Effective stock = stockAvailable − stockReserved. When effectiveStock <= 0, an Agotado badge overlays the image and the add-to-cart button is disabled. When enabled === false on the inventory doc, a No disponible badge is shown instead.

Favorite Toggle

The <FavoriteButton> sits in the top-right corner of the image. It reads state from useFavorites() and calls toggleFavorite(productId) on click, updating both localStorage and Firestore for authenticated users.
Clicking anywhere on the card (except the cart button or favorite button) navigates to /productos/[slug] via an invisible full-card <a> overlay.

Product Detail Page (/productos/[slug])

The detail page is powered by the <ProductDetail> component (src/components/ProductDetail/index.tsx), which wraps a <CartProvider> around <ProductDetailInner>.

Sub-components

ComponentResponsibility
BreadcrumbNavRenders an aria-label="Ruta de navegación" nav with links to InicioProductosDetalle
ProductImageSectionDisplays the full product image with fallback to /product-placeholder.svg
ProductInfoSectionName, description, price / offer price, stock count, add-to-cart button, add-to-favorites button
ReviewSectionLoads and displays all reviews; shows the ReviewForm for authenticated users
ReviewCardSingle review with star rating, comment text, reviewer name, and date
StockAlertButtonRendered when effectiveStock === 0; lets buyers register a stock-back alert

Offer Price Display

When hasOffer is true and offerPrice is a positive number, the detail page shows:
Bs 109.00    (-11%)
~~Bs 123.00~~   ← original price, strikethrough

Stock States

The Agregar al carrito button is enabled. The stock count is shown as Stock: N disponibles.

Reviews

Authenticated buyers can post a star rating (1–5) and a comment via ReviewForm. All reviews are shown in ReviewSection with an aggregate average score. A product with no reviews displays Sin calificaciones and the message Este producto aún no tiene comentarios.
Reviews are stored in the top-level reviews Firestore collection, not as a subcollection of the product document. Each review document contains a productId field that links it to the corresponding product. The detail page shows 4.5 de 5 aggregate score and individual half-star indicators for each review.

Firestore Data Model

products collection

FieldTypeDescription
idstringFirestore document ID
slugstringURL-safe identifier used in /productos/[slug]
namestringDisplay name
descriptionstringFull product description
pricenumberBase price in Bolivianos
imageUrlstring?Product image URL; falls back to /product-placeholder.svg
activebooleanOnly active === true products appear in the catalog
hasOfferbooleanWhether a discounted offer price is active
offerPricenumber?Discounted price shown when hasOffer is true
badgestring?Custom badge label (e.g. “Nuevo”)
soldCountnumberTotal units sold; used for “Popular” ranking
categoryIdstring?Reference to the categories collection
createdAtTimestampUsed for recent sort order

inventory collection

FieldTypeDescription
productIdstringMatches the products document ID
stockAvailablenumberCurrent available stock
stockReservednumberUnits reserved by pending orders
stockTotalnumberTotal stock count
enabledbooleanfalse marks the product as unavailable regardless of stock
Effective available stock is computed as:
const effectiveStock = Math.max(0, stockAvailable - stockReserved);
const isOutOfStock = effectiveStock <= 0;

End-to-End Tests

The catalog is covered by Playwright tests in tests/products/:
product-list.spec.ts
  ✓ load products page                  — title, CategoryFilter, Offers button visible
  ✓ Ofertas                             — ?offers=true activates aria-pressed="true"
  ✓ Categorias                          — ?category=lacteos reflects in URL
  ✓ Buscar                              — ?q=Leche populates search input
  ✓ Search with URL params              — q param round-trips in URL
  ✓ Category filter with URL params
  ✓ Offers filter with URL params       — offer product visible in filtered list
  ✓ Sort by name A-Z                    — ?sort=name-asc persists in URL
  ✓ Combined filters with URL params    — q + category + sort + page all preserved

product-detail.spec.ts
  ✓ loads product with inventory in stock
  ✓ loads product out of stock          — StockAlertButton / "Producto agotado" visible
  ✓ displays offer price when available — Bs 109.00, -11%, Bs 123.00 strikethrough
  ✓ displays product reviews            — ReviewCard with rating, comment, average
  ✓ displays no reviews message
  ✓ product image loads
  ✓ returns 404 for non-existent product
  ✓ displays breadcrumb navigation
  ✓ add to cart button is functional
  ✓ add to cart button is disabled when out of stock

Build docs developers (and LLMs) love