All files / src/components Catalog.jsx

75% Statements 21/28
66.66% Branches 16/24
50% Functions 6/12
77.77% Lines 21/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122                                1x 25x 25x 25x 25x     25x 24x 10x       25x 10x 10x 10x 10x 10x       10x     10x     25x   25x             25x                             6x                       24x                                                                                  
import React, { useState, useEffect } from "react";
import {
  Container,
  Typography,
  Grid,
  Box,
  TextField,
  InputAdornment,
  Chip,
  Stack,
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import Product from "./Product";
import { useLanguage } from "../contexts/LanguageContext";
import { getProducts } from "../db/api";
 
const Catalog = ({ onAddToCart = () => {}, search = "", setSearch = () => {} }) => {
  const { t } = useLanguage();
  const [products, setProducts] = useState([]);
  const [selectedCategory, setSelectedCategory] = useState("");
  const [isLoading, setIsLoading] = useState(true);
 
  // Set initial category when translation is ready
  useEffect(() => {
    if (!selectedCategory && t("catalog.all_categories")) {
        setSelectedCategory(t("catalog.all_categories"));
    }
  }, [t, selectedCategory]);
 
  useEffect(() => {
    const fetchProducts = async () => {
        setIsLoading(true);
        try {
            const data = await getProducts();
            setProducts(Array.isArray(data) ? data : []);
        } catch (error) {
            console.error("Failed to load products from SQLite", error);
        } finally {
            setIsLoading(false);
        }
    };
    fetchProducts();
  }, []);
 
  const categories = [t("catalog.all_categories"), ...new Set(products.map((p) => p.category))];
 
  const filtered = products.filter((p) => {
    const matchSearch = p.name.toLowerCase().includes(search.toLowerCase());
    const matchCategory =
      selectedCategory === t("catalog.all_categories") || p.category === selectedCategory;
    return matchSearch && matchCategory;
  });
 
  return (
    <Container maxWidth="lg">
      <Box id="catalog-header-wrapper" sx={{ mb: 4 }}>
        <Typography variant="h4" component="h1" gutterBottom>
          {t("catalog.title")}
        </Typography>
        <Typography variant="body1" sx={{ color: "#fff", mb: 3 }}>
          {t("catalog.products_found", { count: filtered.length, plural: filtered.length !== 1 ? "s" : "" })}
        </Typography>
 
        <Box id="catalog-search-filters-wrapper" sx={{ display: "flex", gap: 2, flexWrap: "wrap", alignItems: "center", mb: 2 }}>
          <TextField
            size="small"
            placeholder={t("catalog.search_placeholder")}
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            sx={{ minWidth: 240 }}
            InputProps={{
              startAdornment: (
                <InputAdornment position="start">
                  <SearchIcon fontSize="small" />
                </InputAdornment>
              ),
            }}
          />
          <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
            {categories.map((cat) => (
              <Chip
                key={cat}
                label={cat}
                clickable
                color={selectedCategory === cat ? "primary" : "default"}
                onClick={() => setSelectedCategory(cat)}
                variant={selectedCategory === cat ? "filled" : "outlined"}
              />
            ))}
          </Stack>
        </Box>
      </Box>
 
      <Grid container spacing={3}>
        {isLoading ? (
           <Box id="catalog-loading-wrapper" sx={{ width: "100%", textAlign: "center", py: 8 }}>
             <Typography variant="h6" color="text.secondary">
               Carregando produtos...
             </Typography>
           </Box>
        ) : (
          filtered.map((product) => (
            <Grid size={{ xs: 12, sm: 6, md: 4, lg: 3 }} key={product.id}>
              <Product product={product} onAddToCart={onAddToCart} />
            </Grid>
          ))
        )}
      </Grid>
 
      {!isLoading && filtered.length === 0 && (
        <Box id="catalog-empty-wrapper" sx={{ textAlign: "center", py: 8 }}>
          <Typography variant="h6" color="text.secondary">
            {t("catalog.no_products")}
          </Typography>
        </Box>
      )}
    </Container>
  );
};
 
export default Catalog;