Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Jason-AML/MonzaSport-Nextjs/llms.txt

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

The Collection feature is the heart of the Monza Motors shopping experience. Visitors can browse every vehicle in the catalog, narrow results using a price range slider (10k10k–350k) and a model year selector, and click any card to open a full detail page. All catalog data is read from the Supabase vehiculos table and fetched client-side through React Query, giving the page instant stale-while-revalidate caching without a full page reload.

Collection View

The CollectionView component is a client component ("use client") that owns the filtering state and delegates rendering to child Card components. On mount, React Query fires a single request to Supabase via getCollections() and caches the result under the "collections" query key.
const { data, isLoading, error } = useQuery({
  queryKey: ["collections"],
  queryFn: getCollections,
});
While isLoading is true, a loading indicator is shown. If error is set, an error message is rendered across the full grid width. Once data arrives, it is passed through the active filter before being mapped to Card components.

Filtering

The sidebar exposes two controls that work together to reduce the visible vehicle list:
  • Price range — A range slider with min={10000} and max={350000}. Its value is stored in price state (default 350000). A useMemo call recomputes filteredData whenever data or price changes, keeping only vehicles whose precio is less than or equal to the selected value.
  • Year — A <select> element listing 2024, 2023, and 2022 alongside an “All Years” default.
The Clear Filters button calls handleClearFilter, which resets the price slider back to its maximum value of 350000, restoring the full catalog.
const filteredData = useMemo(() => {
  return data?.filter((collection) => collection.precio <= price);
}, [data, price]);

const handleClearFilter = () => {
  setPrice(350000);
};
When filteredData is empty after filtering, a “No vehicles found matching your filters” message spans the full grid width so users know the result set is intentionally empty rather than a loading failure.

Vehicle Card

Each Card receives a single data prop — one row from the vehiculos table. The component renders the following fields:
FieldUsage
nombre_vehiculoCard heading and image alt text
poder_hpPower badge (HP)
aceleracion_0_100Acceleration badge (seconds)
torque_nmTorque badge
precioPrice displayed in the card footer
url_imgVehicle photo (Next.js <Image> with lazy loading)
idHref for the detail link: /collection/{id}
Images use Next.js’s <Image> component with loading="lazy" and a group-hover:scale-110 CSS transition so they zoom in when the user hovers the card. An “In Stock” badge is overlaid on the top-left corner of every card image.

Vehicle Detail Page

Navigating to /collection/[carId] opens a dedicated detail page. The route is a server component (page.js) that:
  1. Awaits params to read carId from the URL.
  2. Calls getCollectionById(carId) to fetch the full vehicle record including joined fabricas and stored relations.
  3. Passes the result as a vehicle prop to the client Detail component.
Dynamic <head> metadata is also generated from the same data fetch using Next.js’s generateMetadata export, so each vehicle page gets a unique <title>, <meta description>, and Open Graph image automatically.
export async function generateMetadata({ params }) {
  const { carId } = await params;
  const vehicle = await getCollectionById(carId);
  return {
    title: vehicle.nombre_vehiculo,
    description: `Detalles del ${vehicle.nombre_vehiculo}`,
    openGraph: {
      title: vehicle.nombre_vehiculo,
      description: `Compra el ${vehicle.nombre_vehiculo}`,
      images: [{ url: vehicle.url_img, alt: vehicle.nombre_vehiculo }],
    },
  };
}
The Detail component renders a full-width image gallery on the left and a sticky pricing card with a “Buy Now” button on the right. The technical specifications grid displays every performance field from the database:
FieldLabel
nombre_vehiculoVehicle name (hero heading)
modeloModel badge overlay on image
anioModel year displayed below the name
precioPrice in the sticky CTA card
motorEngine badge overlay on image + spec grid
poder_hpPower (HP)
aceleracion_0_1000–100 km/h time
velocidad_maximaTop speed (km/h)
torque_nmTorque (Nm)
peso_kgWeight (kg)
url_imgHero background image
fabricasJoined manufacturer record — plant name, city, and country shown in the dealer info panel
storedJoined storage/inventory relation

Data Services

All Supabase queries for the collection feature live in src/services/collectionClient.js. The file exports four functions:
// Fetch the complete vehicle catalog
export const getCollections = async () => {
  const supabase = await createClient();
  const { data, error } = await supabase.from("vehiculos").select("*");
  if (error) throw new Error(error.message);
  return data;
};

// Fetch a single vehicle with fabricas and stored relations
export const getCollectionById = cache(async (id) => {
  const supabase = await createClient();
  const { data, error } = await supabase
    .from("vehiculos")
    .select("*, fabricas(*), stored(*)")
    .eq("id", id)
    .single();
  if (error) throw new Error(error.message);
  return data;
});

// Filter vehicles by model year
export const getCollectionByYear = async (year) => {
  const supabase = await createClient();
  const { data, error } = await supabase
    .from("vehiculos")
    .select("*")
    .eq("anio", year);
  if (error) throw new Error(error.message);
  return data;
};

// Fetch all manufacturer names from the fabricas table
export const getFabricas = async () => {
  const supabase = await createClient();
  const { data, error } = await supabase
    .from("fabricas")
    .select("fabricante");
  if (error) throw new Error(error.message);
  return data;
};
getCollectionById is wrapped in React’s cache() function. This means if generateMetadata and the page component both call getCollectionById(carId) during the same render pass, Supabase is only queried once — the second call returns the in-memory cached result instantly.

Build docs developers (and LLMs) love