Overview
DADDO’s catalog system allows you to share your products publicly through customizable links and QR codes. Catalogs can be filtered by category, include business information, and provide an optimized viewing experience for customers.
Public Catalog Pages
Fetching Catalog Data
The catalog action (src/Redux/actions/Products/catalog_actions.js) retrieves products for a specific user:
export const fetchCatalogByUser = ( userId , category = null ) => async ( dispatch ) => {
try {
dispatch ({ type: "CATALOG_REQUEST" });
const url = category
? `/products/catalogs/ ${ userId } ?category= ${ category } `
: `/products/catalogs/ ${ userId } ` ;
const { data } = await api . get ( url );
dispatch ({
type: "CATALOG_SUCCESS" ,
payload: data ,
});
} catch ( error ) {
dispatch ({
type: "CATALOG_FAILURE" ,
payload: error . response ?. data ?. message || error . message ,
});
}
};
Catalogs are publicly accessible without authentication, making them perfect for sharing with customers.
Catalog URL Structure
Catalog URLs follow this pattern:
/catalog/{userId}?category={categoryId}&showBusiness=1&showPhone=1
Base URL /catalog/{userId} - Shows all products for the user
Category Filter ?category={categoryId} - Filter by specific category
Business Info ?showBusiness=1 - Display business name in header
Contact Info ?showPhone=1 - Show phone number in header
Catalog Page Component
The CatalogPage component (src/Views/CatalogPage.jsx) renders the public catalog view:
const CatalogPage = () => {
const dispatch = useDispatch ();
const { userId } = useParams ();
const [ searchParams ] = useSearchParams ();
const category = searchParams . get ( "category" );
const showBusiness = searchParams . get ( "showBusiness" ) === "1" ;
const showPhone = searchParams . get ( "showPhone" ) === "1" ;
const { catalog , loading , error , businessName , phone } = useSelector (
( state ) => state . catalog
);
useEffect (() => {
if ( userId ) dispatch ( fetchCatalogByUser ( userId , category ));
}, [ userId , category ]);
// Render catalog...
};
Image Carousel
Normalizing Image Data
The catalog handles multiple image formats from different sources:
const normalizeImages = ( images ) => {
if ( ! Array . isArray ( images )) return [];
return images
. map (( it ) => {
if ( typeof it === "string" ) return { url: it };
if ( typeof it === "object" )
return { url: it . url || it . secure_url || it . src || null };
return null ;
})
. filter (( i ) => i ?. url );
};
ImageCarousel Component
Products with multiple images display an interactive carousel:
const ImageCarousel = ({ images = [], fixedHeight = 200 , onImageClick }) => {
const [ currentIndex , setCurrentIndex ] = useState ( 0 );
const imgs = normalizeImages ( images );
const prev = () =>
setCurrentIndex (( i ) => ( i === 0 ? imgs . length - 1 : i - 1 ));
const next = () =>
setCurrentIndex (( i ) => ( i === imgs . length - 1 ? 0 : i + 1 ));
return (
< div className = "w-full" >
< div
className = "relative rounded-xl overflow-hidden bg-gray-900 bg-opacity-25"
style = { { height: fixedHeight } }
>
< img
src = { imgs [ currentIndex ]. url }
alt = ""
onClick = { () => onImageClick ?.( imgs [ currentIndex ]. url ) }
className = "w-full h-full object-contain cursor-pointer"
/>
</ div >
{ imgs . length > 1 && (
< div className = "flex justify-center items-center gap-2 mt-1" >
< button onClick = { prev } className = "p-1 rounded-full bg-gray-900 hover:bg-gray-300" >
< ChevronLeft size = { 16 } />
</ button >
< span className = "text-xs text-gray-300" >
{ currentIndex + 1 } / { imgs . length }
</ span >
< button onClick = { next } className = "p-1 rounded-full bg-gray-900 hover:bg-gray-300" >
< ChevronRight size = { 16 } />
</ button >
</ div >
) }
</ div >
);
};
Previous/Next navigation buttons
Current image indicator (e.g., “2/5”)
Click to enlarge in modal
Automatic reset when product changes
Responsive design
Smooth transitions
Product Card Component
CatalogProductCard
Each product is displayed in a card with variant support:
const CatalogProductCard = ({ product , openModal }) => {
const [ showVariant , setShowVariant ] = useState ( false );
const [ selectedVariant , setSelectedVariant ] = useState ( null );
const productImages = normalizeImages ( product ?. images );
const variants = product ?. variants || [];
const active = showVariant ? selectedVariant : product ;
const handleVariantClick = ( variant ) => {
const vImages = normalizeImages ( variant ?. images );
setSelectedVariant ({
... variant ,
_images: vImages . length ? vImages : productImages ,
});
setShowVariant ( true );
};
return (
< div className = "bg-gray-900 bg-opacity-25 rounded-xl shadow-sm hover:shadow-lg transition p-2" >
< ImageCarousel
images = { active ?. _images ?? productImages }
fixedHeight = { 160 }
onImageClick = { openModal }
/>
< div className = "mt-2 space-y-1" >
< h2 className = "font-sants text-gray-300 text-sm line-clamp-1" >
{ active ?. name }
</ h2 >
< p className = "text-l font-sants text-indigo-400" >
$ { active ?. price }
</ p >
< p className = "text-xs text-gray-500" >
Stock: { active ?. stock }
</ p >
</ div >
{ /* Variant buttons */ }
{ ! showVariant && variants . length > 0 && (
< div className = "flex flex-wrap gap-1 mt-2" >
{ variants . map (( v ) => (
< button
key = { v . id }
onClick = { () => handleVariantClick ( v ) }
className = "text-xs px-2 py-1 border rounded-full hover:bg-indigo-50"
>
{ v . color || v . size || "Variante" }
</ button >
)) }
</ div >
) }
{ showVariant && (
< button
onClick = { () => setShowVariant ( false ) }
className = "text-xs text-indigo-600 mt-2"
>
← Volver
</ button >
) }
</ div >
);
};
When a variant is selected, the card displays the variant’s price, stock, and images instead of the main product details.
Image Modal
Clicking on any product image opens a fullscreen modal:
{ modalImage && (
< div
onClick = { () => setModalImage ( null ) }
className = "fixed inset-0 bg-black/80 flex items-center justify-center p-4"
>
< img
src = { modalImage }
className = "max-h-[90vh] rounded-xl"
/>
</ div >
)}
QR Code Generation
MyCatalogLink Component
The MyCatalogLink component (src/components/MyCatalogLink.jsx) generates shareable catalog links and QR codes:
import { QRCodeCanvas } from "qrcode.react" ;
const MyCatalogLink = ({ onClose }) => {
const user = useSelector (( state ) => state . auth . user );
const { categories } = useSelector (( state ) => state . categories || {});
const [ selectedCategory , setSelectedCategory ] = useState ( "" );
const [ showBusinessOnLink , setShowBusinessOnLink ] = useState ( false );
const [ showPhoneOnLink , setShowPhoneOnLink ] = useState ( false );
const baseUrl = ` ${ window . location . origin } /catalog/ ${ user . id } ` ;
const linkParams = new URLSearchParams ();
if ( selectedCategory ) linkParams . append ( "category" , selectedCategory );
if ( showBusinessOnLink ) linkParams . append ( "showBusiness" , "1" );
if ( showPhoneOnLink ) linkParams . append ( "showPhone" , "1" );
const catalogUrl = linkParams . toString () ? ` ${ baseUrl } ? ${ linkParams } ` : baseUrl ;
return (
< div className = "flex flex-col items-center gap-4" >
< QRCodeCanvas value = { catalogUrl } size = { 140 } />
< a href = { catalogUrl } target = "_blank" rel = "noreferrer" >
{ catalogUrl }
</ a >
</ div >
);
};
QR Code Features
Dynamic URLs QR codes automatically update based on selected filters
High Quality 140x140px canvas for clear scanning
Instant Share Copy link or scan QR code to share
Category Filter Generate QR codes for specific categories
Query Parameters
Catalog URLs support multiple query parameters for customization:
Filter the catalog to show only products from a specific category. /catalog/123?category=456
Display the business name in the catalog header. /catalog/123?showBusiness=1
Display the contact phone number in the catalog header.
Parameter Handling
const [ searchParams ] = useSearchParams ();
const category = searchParams . get ( "category" );
const showBusiness = searchParams . get ( "showBusiness" ) === "1" ;
const showPhone = searchParams . get ( "showPhone" ) === "1" ;
// Display business info conditionally
{ showBusiness && (
< h1 className = "text-2xl font-bold text-gray-800" >
{ businessName }
</ h1 >
)}
{ showPhone && phone && (
< p className = "text-gray-500" > { phone } </ p >
)}
Copy to Clipboard
The catalog link can be copied with one click:
const [ copied , setCopied ] = useState ( false );
const handleCopy = () => {
navigator . clipboard . writeText ( catalogUrl );
setCopied ( true );
setTimeout (() => setCopied ( false ), 2000 );
};
return (
< button onClick = { handleCopy } >
{ copied ? < Check size = { 13 } /> : < Copy size = { 13 } /> }
{ copied ? 'Copiado' : 'Copiar enlace' }
</ button >
);
PDF Export
Users can download their catalog as a PDF:
import { exportCatalogPDF } from "../Redux/actions/Products/export_pdf" ;
const [ includeBusinessName , setIncludeBusinessName ] = useState ( true );
const [ includeOwnerName , setIncludeOwnerName ] = useState ( true );
const [ includePhone , setIncludePhone ] = useState ( true );
const handleDownloadPDF = () => {
dispatch ( exportCatalogPDF ( user . id , {
includeBusinessName ,
includeOwnerName ,
includePhone ,
selectedCategories: selectedCategory ? [ selectedCategory ] : [],
}));
};
PDF export is an async operation that may take a few seconds depending on the number of products and images in the catalog.
Responsive Catalog Grid
The catalog uses a responsive grid layout:
< div className = "
grid
grid-cols-2
sm:grid-cols-2
md:grid-cols-3
lg:grid-cols-4
gap-3
" >
{ catalog ?. map (( product ) => (
< CatalogProductCard
key = { product . id }
product = { product }
openModal = { ( url ) => setModalImage ( url ) }
/>
)) }
</ div >
Catalog List View
The CatalogList component shows all public catalogs from different users:
import { fetchAllCatalogs } from "../Redux/actions/Products/allcatalogs_actions" ;
// View available at /allcatalogs
This feature allows users to discover other businesses using DADDO and browse their product catalogs.
Best Practices
Use high-quality product images
Keep product names concise
Update stock levels regularly
Use category filters for large catalogs
Include business name for branding
Generate QR codes for physical locations
Share links on social media
Include catalog link in email signatures
Print QR codes on business cards
Use category-specific links for promotions
Enable business name for brand recognition
Show phone number for easy contact
Keep catalogs organized with categories
Update product images regularly
Ensure accurate stock information