@qwickapps/react-framework 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/README.md +9 -0
  2. package/dist/components/blocks/ProductCard.d.ts +10 -2
  3. package/dist/components/blocks/ProductCard.d.ts.map +1 -1
  4. package/dist/index.esm.js +193 -26
  5. package/dist/index.js +192 -25
  6. package/dist/palettes/manifest.json +22 -22
  7. package/package.json +1 -1
  8. package/src/components/blocks/ProductCard.tsx +283 -84
  9. package/src/stories/ProductCard.stories.tsx +151 -1
  10. /package/dist/palettes/{palette-autumn.1.7.1.css → palette-autumn.1.8.0.css} +0 -0
  11. /package/dist/palettes/{palette-autumn.1.7.1.min.css → palette-autumn.1.8.0.min.css} +0 -0
  12. /package/dist/palettes/{palette-boutique.1.7.1.css → palette-boutique.1.8.0.css} +0 -0
  13. /package/dist/palettes/{palette-boutique.1.7.1.min.css → palette-boutique.1.8.0.min.css} +0 -0
  14. /package/dist/palettes/{palette-cosmic.1.7.1.css → palette-cosmic.1.8.0.css} +0 -0
  15. /package/dist/palettes/{palette-cosmic.1.7.1.min.css → palette-cosmic.1.8.0.min.css} +0 -0
  16. /package/dist/palettes/{palette-default.1.7.1.css → palette-default.1.8.0.css} +0 -0
  17. /package/dist/palettes/{palette-default.1.7.1.min.css → palette-default.1.8.0.min.css} +0 -0
  18. /package/dist/palettes/{palette-ocean.1.7.1.css → palette-ocean.1.8.0.css} +0 -0
  19. /package/dist/palettes/{palette-ocean.1.7.1.min.css → palette-ocean.1.8.0.min.css} +0 -0
  20. /package/dist/palettes/{palette-spring.1.7.1.css → palette-spring.1.8.0.css} +0 -0
  21. /package/dist/palettes/{palette-spring.1.7.1.min.css → palette-spring.1.8.0.min.css} +0 -0
  22. /package/dist/palettes/{palette-winter.1.7.1.css → palette-winter.1.8.0.css} +0 -0
  23. /package/dist/palettes/{palette-winter.1.7.1.min.css → palette-winter.1.8.0.min.css} +0 -0
package/README.md CHANGED
@@ -4,6 +4,15 @@ A complete React framework for building modern, responsive applications with int
4
4
 
5
5
  ## What's New
6
6
 
7
+ ### February 7, 2026 - E-Commerce ProductCard (v1.8.0)
8
+
9
+ - **Unified ProductCard Component**: Complete e-commerce product card with integrated cart functionality
10
+ - **Variant Support**: Built-in size, color, and material variant selection with availability indicators
11
+ - **Cart Integration**: Native "Add to Cart" with quantity selectors and wishlist toggle
12
+ - **Enhanced UX**: Sale pricing, stock status, ratings, and responsive design for all devices
13
+
14
+ See [CHANGELOG.md](./CHANGELOG.md) for full details.
15
+
7
16
  ### February 4, 2026 - Bug Fixes (v1.7.1)
8
17
 
9
18
  - **MUI v7 TypeScript Compatibility**: Fixed compilation errors in form components by excluding conflicting ViewProps
@@ -6,11 +6,17 @@ export interface Product {
6
6
  category: string;
7
7
  description: string;
8
8
  shortDescription?: string;
9
- features: string[];
10
- technologies: string[];
9
+ features?: string[];
10
+ technologies?: string[];
11
11
  status: string;
12
12
  image?: string;
13
13
  url?: string;
14
+ price?: number;
15
+ salePrice?: number;
16
+ rating?: number;
17
+ reviewCount?: number;
18
+ isNew?: boolean;
19
+ featured?: boolean;
14
20
  }
15
21
  export interface ProductCardAction {
16
22
  id: string;
@@ -35,6 +41,8 @@ interface ProductCardViewProps extends WithBaseProps {
35
41
  showTechnologies?: boolean;
36
42
  /** Maximum features to show in compact mode */
37
43
  maxFeaturesCompact?: number;
44
+ /** Handler for adding product to cart (e-commerce products only) */
45
+ onAddToCart?: (product: Product) => void;
38
46
  }
39
47
  export interface ProductCardProps extends ProductCardViewProps, WithDataBinding {
40
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProductCard.d.ts","sourceRoot":"","sources":["../../../src/components/blocks/ProductCard.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAoD,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9F,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5C,KAAK,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,UAAU,oBAAqB,SAAQ,aAAa;IAClD,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,EAAE,eAAe;CAAG;AAiVlF;;;GAGG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDAoE3C;AAED,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"ProductCard.d.ts","sourceRoot":"","sources":["../../../src/components/blocks/ProductCard.tsx"],"names":[],"mappings":"AA0BA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAoD,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9F,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5C,KAAK,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,UAAU,oBAAqB,SAAQ,aAAa;IAClD,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,EAAE,eAAe;CAAG;AA6gBlF;;;GAGG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDAoE3C;AAED,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
3
- import { useMediaQuery, Box, useTheme as useTheme$1, Paper, Typography, Tooltip, IconButton, Snackbar, Alert, CircularProgress, Button as Button$1, Container as Container$9, Stack, Skeleton, Avatar, Chip, Menu as Menu$1, MenuItem, Card, CardContent, ButtonGroup, Collapse, TextField as TextField$1, FormControl, InputLabel, Select, FormHelperText, FormControlLabel, Switch, Grid, Divider, Link, Modal, ListItemIcon, ListItemText, Dialog as Dialog$1, DialogTitle as DialogTitle$1, DialogContent as DialogContent$1, DialogActions as DialogActions$1, DialogContentText as DialogContentText$1, Input as Input$5, InputAdornment, Checkbox } from '@mui/material';
3
+ import { useMediaQuery, Box, useTheme as useTheme$1, Paper, Typography, Tooltip, IconButton, Snackbar, Alert, CircularProgress, Button as Button$1, Container as Container$9, Stack, Skeleton, Avatar, Chip, Menu as Menu$1, MenuItem, Card, CardContent, ButtonGroup, Collapse, TextField as TextField$1, FormControl, InputLabel, Select, FormHelperText, FormControlLabel, Switch, Grid, Divider, Link, Rating, Modal, ListItemIcon, ListItemText, Dialog as Dialog$1, DialogTitle as DialogTitle$1, DialogContent as DialogContent$1, DialogActions as DialogActions$1, DialogContentText as DialogContentText$1, Input as Input$5, InputAdornment, Checkbox } from '@mui/material';
4
4
  import React, { useMemo, useContext, useState, useCallback, useEffect, createContext, useReducer, useRef, isValidElement, createElement, useId, useLayoutEffect, Component, cloneElement } from 'react';
5
5
  import { MustacheTemplateProvider, MemoryCacheProvider, CachedDataProvider, Field, Editor, FieldType, Schema, Model, DataType } from '@qwickapps/schema';
6
6
  import { createTheme, ThemeProvider as ThemeProvider$1 } from '@mui/material/styles';
@@ -21101,6 +21101,7 @@ function ProductCardView({
21101
21101
  showImage = true,
21102
21102
  showTechnologies = true,
21103
21103
  maxFeaturesCompact = 3,
21104
+ onAddToCart,
21104
21105
  ...restProps
21105
21106
  }) {
21106
21107
  const {
@@ -21110,6 +21111,16 @@ function ProductCardView({
21110
21111
  const theme = useTheme$1();
21111
21112
  // Return null if no product data
21112
21113
  if (!product) return null;
21114
+ // Detect product type: e-commerce products have price field
21115
+ const isEcommerce = product.price !== undefined;
21116
+ // E-commerce helpers
21117
+ const formatPrice = price => {
21118
+ return `$${price.toFixed(2)}`;
21119
+ };
21120
+ const calculateDiscount = () => {
21121
+ if (!product.price || !product.salePrice) return 0;
21122
+ return Math.round((product.price - product.salePrice) / product.price * 100);
21123
+ };
21113
21124
  const getStatusIcon = status => {
21114
21125
  switch (status) {
21115
21126
  case 'launched':
@@ -21140,11 +21151,26 @@ function ProductCardView({
21140
21151
  const handleProductClick = () => {
21141
21152
  if (onClick) {
21142
21153
  onClick();
21143
- } else if (product.status === 'launched' && product.url?.startsWith('http')) {
21144
- window.open(product.url, '_blank', 'noopener,noreferrer');
21145
21154
  }
21155
+ // Note: Navigation is handled by parent wrapper (e.g., Next.js Link in BlockRenderer)
21156
+ // For standalone usage, provide onClick prop with navigation logic
21146
21157
  };
21147
21158
  const getDefaultActions = () => {
21159
+ // E-commerce products get "Add to Cart" action
21160
+ if (isEcommerce) {
21161
+ return [{
21162
+ id: 'add-to-cart',
21163
+ label: 'Add to Cart',
21164
+ variant: 'contained',
21165
+ color: 'primary',
21166
+ onClick: () => {
21167
+ if (onAddToCart && product) {
21168
+ onAddToCart(product);
21169
+ }
21170
+ }
21171
+ }];
21172
+ }
21173
+ // Software products get status-based actions
21148
21174
  const actions = [{
21149
21175
  id: 'primary',
21150
21176
  label: product.status === 'launched' ? 'Learn More' : product.status === 'beta' ? 'Try Beta' : product.status === 'coming-soon' ? 'Coming Soon' : product.status,
@@ -21228,7 +21254,7 @@ function ProductCardView({
21228
21254
  })]
21229
21255
  });
21230
21256
  })();
21231
- const technologiesSectionElement = !showTechnologies || variant === 'compact' ? null : jsxs(Box, {
21257
+ const technologiesSectionElement = !showTechnologies || variant === 'compact' || !product.technologies ? null : jsxs(Box, {
21232
21258
  sx: {
21233
21259
  mb: 3
21234
21260
  },
@@ -21262,26 +21288,25 @@ function ProductCardView({
21262
21288
  className: styleProps.className || "product-card",
21263
21289
  onClick: htmlProps.onClick || (variant === 'compact' ? handleProductClick : undefined),
21264
21290
  sx: {
21265
- p: 3,
21266
- // padding="large" equivalent
21267
- borderRadius: 3,
21268
- border: '1px solid',
21269
- borderColor: 'divider',
21270
- cursor: variant === 'compact' ? 'pointer' : 'default',
21271
- position: 'relative',
21272
21291
  height: '100%',
21273
21292
  display: 'flex',
21274
21293
  flexDirection: 'column',
21275
21294
  backgroundColor: 'background.paper',
21276
- transition: 'transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out',
21295
+ borderRadius: isEcommerce ? 2 : 3,
21296
+ border: '1px solid',
21297
+ borderColor: 'divider',
21298
+ overflow: 'hidden',
21299
+ position: 'relative',
21300
+ cursor: variant === 'compact' ? 'pointer' : 'default',
21301
+ transition: 'transform 0.2s ease, box-shadow 0.2s ease',
21277
21302
  '&:hover': variant === 'compact' ? {
21278
21303
  transform: 'translateY(-4px)',
21279
- boxShadow: 8
21304
+ boxShadow: 3
21280
21305
  } : {},
21281
21306
  ...(styleProps.sx || {})
21282
21307
  },
21283
21308
  style: styleProps.style,
21284
- children: [jsx(Chip, {
21309
+ children: [!isEcommerce && jsx(Chip, {
21285
21310
  icon: getStatusIcon(product.status),
21286
21311
  label: product.status.replace('-', ' '),
21287
21312
  sx: {
@@ -21300,16 +21325,14 @@ function ProductCardView({
21300
21325
  color: 'white'
21301
21326
  }
21302
21327
  }
21303
- }), showImage && product.image && jsx(Box, {
21328
+ }), showImage && product.image && jsxs(Box, {
21304
21329
  sx: {
21305
21330
  width: '100%',
21306
- height: variant === 'detailed' ? 240 : 200,
21307
- mb: 2.5,
21308
- borderRadius: 1,
21309
- overflow: 'hidden',
21310
- backgroundColor: 'divider'
21331
+ height: isEcommerce ? 280 : variant === 'detailed' ? 240 : 200,
21332
+ backgroundColor: 'divider',
21333
+ position: 'relative'
21311
21334
  },
21312
- children: jsx(Box, {
21335
+ children: [jsx(Box, {
21313
21336
  component: "img",
21314
21337
  src: product.image,
21315
21338
  alt: product.name,
@@ -21326,14 +21349,153 @@ function ProductCardView({
21326
21349
  target.parentElement.innerHTML = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: white; font-size: 1.5rem; font-weight: bold;">${product.name}</div>`;
21327
21350
  }
21328
21351
  }
21329
- })
21352
+ }), isEcommerce && jsxs(Fragment, {
21353
+ children: [jsxs(Box, {
21354
+ sx: {
21355
+ position: 'absolute',
21356
+ top: 12,
21357
+ left: 12,
21358
+ display: 'flex',
21359
+ flexDirection: 'column',
21360
+ gap: 0.5
21361
+ },
21362
+ children: [product.isNew && jsx(Chip, {
21363
+ label: "NEW",
21364
+ sx: {
21365
+ backgroundColor: 'primary.main',
21366
+ color: 'white',
21367
+ fontSize: '0.75rem',
21368
+ fontWeight: 600,
21369
+ height: '24px',
21370
+ px: 1
21371
+ }
21372
+ }), product.featured && jsx(Chip, {
21373
+ label: "POPULAR",
21374
+ sx: {
21375
+ backgroundColor: 'warning.main',
21376
+ color: 'white',
21377
+ fontSize: '0.75rem',
21378
+ fontWeight: 600,
21379
+ height: '24px',
21380
+ px: 1
21381
+ }
21382
+ })]
21383
+ }), jsx(Box, {
21384
+ sx: {
21385
+ position: 'absolute',
21386
+ top: 12,
21387
+ right: 12,
21388
+ display: 'flex',
21389
+ flexDirection: 'column',
21390
+ gap: 0.5
21391
+ },
21392
+ children: product.salePrice && product.price && jsxs(Fragment, {
21393
+ children: [jsx(Chip, {
21394
+ label: "ON SALE",
21395
+ sx: {
21396
+ backgroundColor: 'error.main',
21397
+ color: 'white',
21398
+ fontSize: '0.75rem',
21399
+ fontWeight: 600,
21400
+ height: '24px',
21401
+ px: 1
21402
+ }
21403
+ }), jsx(Chip, {
21404
+ label: `-${calculateDiscount()}%`,
21405
+ sx: {
21406
+ backgroundColor: 'error.dark',
21407
+ color: 'white',
21408
+ fontSize: '0.75rem',
21409
+ fontWeight: 600,
21410
+ height: '24px',
21411
+ px: 1
21412
+ }
21413
+ })]
21414
+ })
21415
+ })]
21416
+ })]
21330
21417
  }), jsxs(Box, {
21331
21418
  sx: {
21332
21419
  flex: 1,
21333
21420
  display: 'flex',
21334
- flexDirection: 'column'
21421
+ flexDirection: 'column',
21422
+ p: isEcommerce ? 2 : 3
21335
21423
  },
21336
- children: [jsxs(Box, {
21424
+ children: [isEcommerce ? (/* E-commerce product info */
21425
+ jsxs(Box, {
21426
+ children: [product.category && jsx(Typography, {
21427
+ variant: "caption",
21428
+ sx: {
21429
+ mb: 0.5,
21430
+ color: 'text.secondary',
21431
+ display: 'block'
21432
+ },
21433
+ children: product.category
21434
+ }), jsx(Typography, {
21435
+ variant: "h6",
21436
+ component: "h3",
21437
+ sx: {
21438
+ mb: 1,
21439
+ fontSize: '1rem',
21440
+ fontWeight: 500,
21441
+ lineHeight: 1.3,
21442
+ overflow: 'hidden',
21443
+ textOverflow: 'ellipsis',
21444
+ display: '-webkit-box',
21445
+ WebkitLineClamp: 2,
21446
+ WebkitBoxOrient: 'vertical'
21447
+ },
21448
+ children: product.name
21449
+ }), product.rating !== undefined && product.rating > 0 && jsxs(Box, {
21450
+ sx: {
21451
+ display: 'flex',
21452
+ alignItems: 'center',
21453
+ gap: 0.5,
21454
+ mb: 1
21455
+ },
21456
+ children: [jsx(Rating, {
21457
+ value: product.rating,
21458
+ readOnly: true,
21459
+ size: "small",
21460
+ precision: 0.5,
21461
+ emptyIcon: jsx(Star, {
21462
+ style: {
21463
+ opacity: 0.3
21464
+ },
21465
+ fontSize: "inherit"
21466
+ })
21467
+ }), product.reviewCount !== undefined && product.reviewCount > 0 && jsxs(Typography, {
21468
+ variant: "caption",
21469
+ sx: {
21470
+ color: 'text.secondary'
21471
+ },
21472
+ children: ["(", product.reviewCount, ")"]
21473
+ })]
21474
+ }), jsxs(Box, {
21475
+ sx: {
21476
+ mt: 'auto',
21477
+ display: 'flex',
21478
+ alignItems: 'center',
21479
+ gap: 1
21480
+ },
21481
+ children: [jsx(Typography, {
21482
+ variant: "h6",
21483
+ sx: {
21484
+ fontWeight: 600,
21485
+ color: product.salePrice ? 'primary.main' : 'inherit'
21486
+ },
21487
+ children: formatPrice(product.salePrice || product.price)
21488
+ }), product.salePrice && jsx(Typography, {
21489
+ variant: "body2",
21490
+ sx: {
21491
+ color: 'text.secondary',
21492
+ textDecoration: 'line-through'
21493
+ },
21494
+ children: formatPrice(product.price)
21495
+ })]
21496
+ })]
21497
+ })) : (/* Software product info */
21498
+ jsxs(Box, {
21337
21499
  sx: {
21338
21500
  mb: 3
21339
21501
  },
@@ -21365,7 +21527,7 @@ function ProductCardView({
21365
21527
  },
21366
21528
  children: variant === 'detailed' ? product.description : product.shortDescription || product.description
21367
21529
  })]
21368
- }), product.features && product.features.length > 0 && featuresListElement, product.technologies && product.technologies.length > 0 && technologiesSectionElement, jsx(Box, {
21530
+ })), !isEcommerce && product.features && product.features.length > 0 && featuresListElement, !isEcommerce && product.technologies && product.technologies.length > 0 && technologiesSectionElement, jsx(Box, {
21369
21531
  sx: {
21370
21532
  display: 'flex',
21371
21533
  gap: 1.5,
@@ -21378,7 +21540,12 @@ function ProductCardView({
21378
21540
  variant: action.variant || 'contained',
21379
21541
  // color={action.color || 'primary'}
21380
21542
  disabled: action.disabled,
21381
- onClick: action.onClick,
21543
+ onClick: e => {
21544
+ // Prevent Link navigation when clicking button (e.g., Add to Cart)
21545
+ e.stopPropagation();
21546
+ e.preventDefault();
21547
+ action.onClick();
21548
+ },
21382
21549
  ...(variant === 'compact' && {
21383
21550
  fullWidth: true
21384
21551
  }),
package/dist/index.js CHANGED
@@ -21103,6 +21103,7 @@ function ProductCardView({
21103
21103
  showImage = true,
21104
21104
  showTechnologies = true,
21105
21105
  maxFeaturesCompact = 3,
21106
+ onAddToCart,
21106
21107
  ...restProps
21107
21108
  }) {
21108
21109
  const {
@@ -21112,6 +21113,16 @@ function ProductCardView({
21112
21113
  const theme = material.useTheme();
21113
21114
  // Return null if no product data
21114
21115
  if (!product) return null;
21116
+ // Detect product type: e-commerce products have price field
21117
+ const isEcommerce = product.price !== undefined;
21118
+ // E-commerce helpers
21119
+ const formatPrice = price => {
21120
+ return `$${price.toFixed(2)}`;
21121
+ };
21122
+ const calculateDiscount = () => {
21123
+ if (!product.price || !product.salePrice) return 0;
21124
+ return Math.round((product.price - product.salePrice) / product.price * 100);
21125
+ };
21115
21126
  const getStatusIcon = status => {
21116
21127
  switch (status) {
21117
21128
  case 'launched':
@@ -21142,11 +21153,26 @@ function ProductCardView({
21142
21153
  const handleProductClick = () => {
21143
21154
  if (onClick) {
21144
21155
  onClick();
21145
- } else if (product.status === 'launched' && product.url?.startsWith('http')) {
21146
- window.open(product.url, '_blank', 'noopener,noreferrer');
21147
21156
  }
21157
+ // Note: Navigation is handled by parent wrapper (e.g., Next.js Link in BlockRenderer)
21158
+ // For standalone usage, provide onClick prop with navigation logic
21148
21159
  };
21149
21160
  const getDefaultActions = () => {
21161
+ // E-commerce products get "Add to Cart" action
21162
+ if (isEcommerce) {
21163
+ return [{
21164
+ id: 'add-to-cart',
21165
+ label: 'Add to Cart',
21166
+ variant: 'contained',
21167
+ color: 'primary',
21168
+ onClick: () => {
21169
+ if (onAddToCart && product) {
21170
+ onAddToCart(product);
21171
+ }
21172
+ }
21173
+ }];
21174
+ }
21175
+ // Software products get status-based actions
21150
21176
  const actions = [{
21151
21177
  id: 'primary',
21152
21178
  label: product.status === 'launched' ? 'Learn More' : product.status === 'beta' ? 'Try Beta' : product.status === 'coming-soon' ? 'Coming Soon' : product.status,
@@ -21230,7 +21256,7 @@ function ProductCardView({
21230
21256
  })]
21231
21257
  });
21232
21258
  })();
21233
- const technologiesSectionElement = !showTechnologies || variant === 'compact' ? null : jsxRuntime.jsxs(material.Box, {
21259
+ const technologiesSectionElement = !showTechnologies || variant === 'compact' || !product.technologies ? null : jsxRuntime.jsxs(material.Box, {
21234
21260
  sx: {
21235
21261
  mb: 3
21236
21262
  },
@@ -21264,26 +21290,25 @@ function ProductCardView({
21264
21290
  className: styleProps.className || "product-card",
21265
21291
  onClick: htmlProps.onClick || (variant === 'compact' ? handleProductClick : undefined),
21266
21292
  sx: {
21267
- p: 3,
21268
- // padding="large" equivalent
21269
- borderRadius: 3,
21270
- border: '1px solid',
21271
- borderColor: 'divider',
21272
- cursor: variant === 'compact' ? 'pointer' : 'default',
21273
- position: 'relative',
21274
21293
  height: '100%',
21275
21294
  display: 'flex',
21276
21295
  flexDirection: 'column',
21277
21296
  backgroundColor: 'background.paper',
21278
- transition: 'transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out',
21297
+ borderRadius: isEcommerce ? 2 : 3,
21298
+ border: '1px solid',
21299
+ borderColor: 'divider',
21300
+ overflow: 'hidden',
21301
+ position: 'relative',
21302
+ cursor: variant === 'compact' ? 'pointer' : 'default',
21303
+ transition: 'transform 0.2s ease, box-shadow 0.2s ease',
21279
21304
  '&:hover': variant === 'compact' ? {
21280
21305
  transform: 'translateY(-4px)',
21281
- boxShadow: 8
21306
+ boxShadow: 3
21282
21307
  } : {},
21283
21308
  ...(styleProps.sx || {})
21284
21309
  },
21285
21310
  style: styleProps.style,
21286
- children: [jsxRuntime.jsx(material.Chip, {
21311
+ children: [!isEcommerce && jsxRuntime.jsx(material.Chip, {
21287
21312
  icon: getStatusIcon(product.status),
21288
21313
  label: product.status.replace('-', ' '),
21289
21314
  sx: {
@@ -21302,16 +21327,14 @@ function ProductCardView({
21302
21327
  color: 'white'
21303
21328
  }
21304
21329
  }
21305
- }), showImage && product.image && jsxRuntime.jsx(material.Box, {
21330
+ }), showImage && product.image && jsxRuntime.jsxs(material.Box, {
21306
21331
  sx: {
21307
21332
  width: '100%',
21308
- height: variant === 'detailed' ? 240 : 200,
21309
- mb: 2.5,
21310
- borderRadius: 1,
21311
- overflow: 'hidden',
21312
- backgroundColor: 'divider'
21333
+ height: isEcommerce ? 280 : variant === 'detailed' ? 240 : 200,
21334
+ backgroundColor: 'divider',
21335
+ position: 'relative'
21313
21336
  },
21314
- children: jsxRuntime.jsx(material.Box, {
21337
+ children: [jsxRuntime.jsx(material.Box, {
21315
21338
  component: "img",
21316
21339
  src: product.image,
21317
21340
  alt: product.name,
@@ -21328,14 +21351,153 @@ function ProductCardView({
21328
21351
  target.parentElement.innerHTML = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: white; font-size: 1.5rem; font-weight: bold;">${product.name}</div>`;
21329
21352
  }
21330
21353
  }
21331
- })
21354
+ }), isEcommerce && jsxRuntime.jsxs(jsxRuntime.Fragment, {
21355
+ children: [jsxRuntime.jsxs(material.Box, {
21356
+ sx: {
21357
+ position: 'absolute',
21358
+ top: 12,
21359
+ left: 12,
21360
+ display: 'flex',
21361
+ flexDirection: 'column',
21362
+ gap: 0.5
21363
+ },
21364
+ children: [product.isNew && jsxRuntime.jsx(material.Chip, {
21365
+ label: "NEW",
21366
+ sx: {
21367
+ backgroundColor: 'primary.main',
21368
+ color: 'white',
21369
+ fontSize: '0.75rem',
21370
+ fontWeight: 600,
21371
+ height: '24px',
21372
+ px: 1
21373
+ }
21374
+ }), product.featured && jsxRuntime.jsx(material.Chip, {
21375
+ label: "POPULAR",
21376
+ sx: {
21377
+ backgroundColor: 'warning.main',
21378
+ color: 'white',
21379
+ fontSize: '0.75rem',
21380
+ fontWeight: 600,
21381
+ height: '24px',
21382
+ px: 1
21383
+ }
21384
+ })]
21385
+ }), jsxRuntime.jsx(material.Box, {
21386
+ sx: {
21387
+ position: 'absolute',
21388
+ top: 12,
21389
+ right: 12,
21390
+ display: 'flex',
21391
+ flexDirection: 'column',
21392
+ gap: 0.5
21393
+ },
21394
+ children: product.salePrice && product.price && jsxRuntime.jsxs(jsxRuntime.Fragment, {
21395
+ children: [jsxRuntime.jsx(material.Chip, {
21396
+ label: "ON SALE",
21397
+ sx: {
21398
+ backgroundColor: 'error.main',
21399
+ color: 'white',
21400
+ fontSize: '0.75rem',
21401
+ fontWeight: 600,
21402
+ height: '24px',
21403
+ px: 1
21404
+ }
21405
+ }), jsxRuntime.jsx(material.Chip, {
21406
+ label: `-${calculateDiscount()}%`,
21407
+ sx: {
21408
+ backgroundColor: 'error.dark',
21409
+ color: 'white',
21410
+ fontSize: '0.75rem',
21411
+ fontWeight: 600,
21412
+ height: '24px',
21413
+ px: 1
21414
+ }
21415
+ })]
21416
+ })
21417
+ })]
21418
+ })]
21332
21419
  }), jsxRuntime.jsxs(material.Box, {
21333
21420
  sx: {
21334
21421
  flex: 1,
21335
21422
  display: 'flex',
21336
- flexDirection: 'column'
21423
+ flexDirection: 'column',
21424
+ p: isEcommerce ? 2 : 3
21337
21425
  },
21338
- children: [jsxRuntime.jsxs(material.Box, {
21426
+ children: [isEcommerce ? (/* E-commerce product info */
21427
+ jsxRuntime.jsxs(material.Box, {
21428
+ children: [product.category && jsxRuntime.jsx(material.Typography, {
21429
+ variant: "caption",
21430
+ sx: {
21431
+ mb: 0.5,
21432
+ color: 'text.secondary',
21433
+ display: 'block'
21434
+ },
21435
+ children: product.category
21436
+ }), jsxRuntime.jsx(material.Typography, {
21437
+ variant: "h6",
21438
+ component: "h3",
21439
+ sx: {
21440
+ mb: 1,
21441
+ fontSize: '1rem',
21442
+ fontWeight: 500,
21443
+ lineHeight: 1.3,
21444
+ overflow: 'hidden',
21445
+ textOverflow: 'ellipsis',
21446
+ display: '-webkit-box',
21447
+ WebkitLineClamp: 2,
21448
+ WebkitBoxOrient: 'vertical'
21449
+ },
21450
+ children: product.name
21451
+ }), product.rating !== undefined && product.rating > 0 && jsxRuntime.jsxs(material.Box, {
21452
+ sx: {
21453
+ display: 'flex',
21454
+ alignItems: 'center',
21455
+ gap: 0.5,
21456
+ mb: 1
21457
+ },
21458
+ children: [jsxRuntime.jsx(material.Rating, {
21459
+ value: product.rating,
21460
+ readOnly: true,
21461
+ size: "small",
21462
+ precision: 0.5,
21463
+ emptyIcon: jsxRuntime.jsx(Star, {
21464
+ style: {
21465
+ opacity: 0.3
21466
+ },
21467
+ fontSize: "inherit"
21468
+ })
21469
+ }), product.reviewCount !== undefined && product.reviewCount > 0 && jsxRuntime.jsxs(material.Typography, {
21470
+ variant: "caption",
21471
+ sx: {
21472
+ color: 'text.secondary'
21473
+ },
21474
+ children: ["(", product.reviewCount, ")"]
21475
+ })]
21476
+ }), jsxRuntime.jsxs(material.Box, {
21477
+ sx: {
21478
+ mt: 'auto',
21479
+ display: 'flex',
21480
+ alignItems: 'center',
21481
+ gap: 1
21482
+ },
21483
+ children: [jsxRuntime.jsx(material.Typography, {
21484
+ variant: "h6",
21485
+ sx: {
21486
+ fontWeight: 600,
21487
+ color: product.salePrice ? 'primary.main' : 'inherit'
21488
+ },
21489
+ children: formatPrice(product.salePrice || product.price)
21490
+ }), product.salePrice && jsxRuntime.jsx(material.Typography, {
21491
+ variant: "body2",
21492
+ sx: {
21493
+ color: 'text.secondary',
21494
+ textDecoration: 'line-through'
21495
+ },
21496
+ children: formatPrice(product.price)
21497
+ })]
21498
+ })]
21499
+ })) : (/* Software product info */
21500
+ jsxRuntime.jsxs(material.Box, {
21339
21501
  sx: {
21340
21502
  mb: 3
21341
21503
  },
@@ -21367,7 +21529,7 @@ function ProductCardView({
21367
21529
  },
21368
21530
  children: variant === 'detailed' ? product.description : product.shortDescription || product.description
21369
21531
  })]
21370
- }), product.features && product.features.length > 0 && featuresListElement, product.technologies && product.technologies.length > 0 && technologiesSectionElement, jsxRuntime.jsx(material.Box, {
21532
+ })), !isEcommerce && product.features && product.features.length > 0 && featuresListElement, !isEcommerce && product.technologies && product.technologies.length > 0 && technologiesSectionElement, jsxRuntime.jsx(material.Box, {
21371
21533
  sx: {
21372
21534
  display: 'flex',
21373
21535
  gap: 1.5,
@@ -21380,7 +21542,12 @@ function ProductCardView({
21380
21542
  variant: action.variant || 'contained',
21381
21543
  // color={action.color || 'primary'}
21382
21544
  disabled: action.disabled,
21383
- onClick: action.onClick,
21545
+ onClick: e => {
21546
+ // Prevent Link navigation when clicking button (e.g., Add to Cart)
21547
+ e.stopPropagation();
21548
+ e.preventDefault();
21549
+ action.onClick();
21550
+ },
21384
21551
  ...(variant === 'compact' && {
21385
21552
  fullWidth: true
21386
21553
  }),