@beyondcorp/beyond-ui 1.2.26 → 1.2.30
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.
- package/dist/components/AllProductsView/AllProductsView.d.ts +0 -11
- package/dist/components/AllProductsView/AllProductsView.js +2 -9
- package/dist/components/AllProductsView/AllProductsView.js.map +1 -1
- package/dist/components/AllProductsView/ProductCard.js +4 -2
- package/dist/components/AllProductsView/ProductCard.js.map +1 -1
- package/dist/components/Card/Card.js +1 -1
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/components/DashboardGrid/DashboardGrid.d.ts +3 -22
- package/dist/components/DashboardGrid/DashboardGrid.js +10 -48
- package/dist/components/DashboardGrid/DashboardGrid.js.map +1 -1
- package/dist/components/Marketplace/AllProductsView.d.ts +8 -0
- package/dist/components/Marketplace/AllProductsView.js +185 -0
- package/dist/components/Marketplace/AllProductsView.js.map +1 -0
- package/dist/components/Marketplace/CheckoutCompact.d.ts +10 -0
- package/dist/components/Marketplace/CheckoutCompact.js +66 -0
- package/dist/components/Marketplace/CheckoutCompact.js.map +1 -0
- package/dist/components/Marketplace/CheckoutComponent.d.ts +8 -0
- package/dist/components/Marketplace/CheckoutComponent.js +123 -0
- package/dist/components/Marketplace/CheckoutComponent.js.map +1 -0
- package/dist/components/Marketplace/MarketplaceComponent.d.ts +8 -0
- package/dist/components/Marketplace/MarketplaceComponent.js +108 -0
- package/dist/components/Marketplace/MarketplaceComponent.js.map +1 -0
- package/dist/components/Marketplace/MarketplaceSidebar.d.ts +12 -0
- package/dist/components/Marketplace/MarketplaceSidebar.js +167 -0
- package/dist/components/Marketplace/MarketplaceSidebar.js.map +1 -0
- package/dist/components/Marketplace/SingleProductView.d.ts +9 -0
- package/dist/components/Marketplace/SingleProductView.js +52 -0
- package/dist/components/Marketplace/SingleProductView.js.map +1 -0
- package/dist/components/Marketplace/data/sampleData.d.ts +3 -0
- package/dist/components/Marketplace/data/sampleData.js +153 -0
- package/dist/components/Marketplace/data/sampleData.js.map +1 -0
- package/dist/components/Marketplace/index.d.ts +7 -0
- package/dist/components/Marketplace/types.d.ts +82 -0
- package/dist/components/MarketplaceLayout/MarketplaceLayout.d.ts +0 -10
- package/dist/components/MarketplaceLayout/MarketplaceLayout.js +3 -3
- package/dist/components/MarketplaceLayout/MarketplaceLayout.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +6 -5
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ProductData } from '../SingleProductView/SingleProductView';
|
|
3
|
-
/**
|
|
4
|
-
* AllProductsView
|
|
5
|
-
* - Renders a responsive product grid using DashboardGrid.
|
|
6
|
-
* - By default, uses 1 column (sm), 2 columns (md), and 4 columns (lg/xl/2xl) for optimal card sizing.
|
|
7
|
-
* - UI library consumers can override the number of columns via the `columns` prop.
|
|
8
|
-
* - Ensures ProductCard maintains a usable min/max width for visual consistency.
|
|
9
|
-
* - All logic is theme-agnostic and leverages reusable hooks/components.
|
|
10
|
-
*/
|
|
11
3
|
export interface AllProductsViewProps {
|
|
12
4
|
products: ProductData[];
|
|
13
5
|
onProductClick?: (productId: string) => void;
|
|
@@ -18,8 +10,5 @@ export interface AllProductsViewProps {
|
|
|
18
10
|
enableFilter?: boolean;
|
|
19
11
|
enableSort?: boolean;
|
|
20
12
|
className?: string;
|
|
21
|
-
columns?: number;
|
|
22
|
-
itemMinWidth?: string;
|
|
23
|
-
itemMaxWidth?: string;
|
|
24
13
|
}
|
|
25
14
|
export declare const AllProductsView: React.FC<AllProductsViewProps>;
|
|
@@ -16,20 +16,13 @@ import { cn } from '../../utils/cn.js';
|
|
|
16
16
|
import { ProductCard } from './ProductCard.js';
|
|
17
17
|
import { DashboardGrid } from '../DashboardGrid/DashboardGrid.js';
|
|
18
18
|
|
|
19
|
-
const AllProductsView = ({ products, onProductClick, onAddToCart, onWishlist, onShare, enableSearch = true, enableFilter = true, enableSort = true, className,
|
|
19
|
+
const AllProductsView = ({ products, onProductClick, onAddToCart, onWishlist, onShare, enableSearch = true, enableFilter = true, enableSort = true, className, }) => {
|
|
20
20
|
const [search, setSearch] = useState('');
|
|
21
21
|
const debouncedSearch = useDebounce(search, 300);
|
|
22
22
|
const [sort, setSort] = useState('featured');
|
|
23
23
|
const [filter, setFilter] = useState('all');
|
|
24
24
|
const { currentBreakpoint, isBelow } = useBreakpoint();
|
|
25
25
|
isBelow('md');
|
|
26
|
-
(() => {
|
|
27
|
-
if (isBelow('md'))
|
|
28
|
-
return 1;
|
|
29
|
-
if (isBelow('lg'))
|
|
30
|
-
return 2;
|
|
31
|
-
return 4; // lg, xl, 2xl
|
|
32
|
-
})();
|
|
33
26
|
// Filter and sort products
|
|
34
27
|
const filteredProducts = useMemo(() => {
|
|
35
28
|
let result = products;
|
|
@@ -61,7 +54,7 @@ const AllProductsView = ({ products, onProductClick, onAddToCart, onWishlist, on
|
|
|
61
54
|
{ value: 'featured', label: 'Featured' },
|
|
62
55
|
{ value: 'price-asc', label: 'Price: Low to High' },
|
|
63
56
|
{ value: 'price-desc', label: 'Price: High to Low' },
|
|
64
|
-
], className: "w-full sm:w-40" }))] }) }) }) }), jsx(PageLayoutContent, { layout: "centered", spacing: "lg", children: jsx(DashboardGrid, { columns:
|
|
57
|
+
], className: "w-full sm:w-40" }))] }) }) }) }), jsx(PageLayoutContent, { layout: "centered", spacing: "lg", children: jsx(DashboardGrid, { columns: 3, children: filteredProducts.length === 0 ? (jsx("div", { className: "col-span-full text-center text-gray-500 py-12", children: "No products found." })) : (filteredProducts.map((product) => (jsx(ProductCard, { product: product, onClick: () => onProductClick && onProductClick(product.id), onAddToCart: () => onAddToCart && onAddToCart(product.id), onWishlist: () => onWishlist && onWishlist(product.id), onShare: () => onShare && onShare(product.id) }, product.id)))) }) })] }));
|
|
65
58
|
};
|
|
66
59
|
|
|
67
60
|
export { AllProductsView };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AllProductsView.js","sources":["../../../src/components/AllProductsView/AllProductsView.tsx"],"sourcesContent":["import React, { useState, useMemo } from 'react';\r\nimport { PageLayout, PageHeader, PageLayoutContent } from '../PageLayout';\r\nimport { Input } from '../Input';\r\nimport { Select } from '../Select';\r\nimport { useDebounce } from '../../hooks/useDebounce';\r\nimport { useBreakpoint } from '../../hooks/useBreakpoint';\r\nimport { cn } from '../../utils/cn';\r\nimport { ProductData } from '../SingleProductView/SingleProductView';\r\nimport { ProductCard } from './ProductCard';\r\nimport { DashboardGrid } from '../DashboardGrid/DashboardGrid';\r\n\r\n/**\r\n * AllProductsView\r\n * - Renders a responsive product grid using DashboardGrid.\r\n * - By default, uses 1 column (sm), 2 columns (md), and 4 columns (lg/xl/2xl) for optimal card sizing.\r\n * - UI library consumers can override the number of columns via the `columns` prop.\r\n * - Ensures ProductCard maintains a usable min/max width for visual consistency.\r\n * - All logic is theme-agnostic and leverages reusable hooks/components.\r\n */\r\nexport interface AllProductsViewProps {\r\n products: ProductData[];\r\n onProductClick?: (productId: string) => void;\r\n onAddToCart?: (productId: string) => void;\r\n onWishlist?: (productId: string) => void;\r\n onShare?: (productId: string) => void;\r\n enableSearch?: boolean;\r\n enableFilter?: boolean;\r\n enableSort?: boolean;\r\n className?: string;\r\n columns?: number; // Allows consumer to override grid columns\r\n itemMinWidth?: string; // Optional min width for grid items\r\n itemMaxWidth?: string; // Optional max width for grid items\r\n}\r\n\r\nexport const AllProductsView: React.FC<AllProductsViewProps> = ({\r\n products,\r\n onProductClick,\r\n onAddToCart,\r\n onWishlist,\r\n onShare,\r\n enableSearch = true,\r\n enableFilter = true,\r\n enableSort = true,\r\n className,\r\n columns,\r\n itemMinWidth = \"220px\",\r\n itemMaxWidth = \"320px\",\r\n}) => {\r\n const [search, setSearch] = useState('');\r\n const debouncedSearch = useDebounce(search, 300);\r\n const [sort, setSort] = useState('featured');\r\n const [filter, setFilter] = useState('all');\r\n const { currentBreakpoint, isBelow } = useBreakpoint();\r\n const isMobile = isBelow('md');\r\n\r\n // Responsive columns logic\r\n const allowedColumns = [1, 2, 3, 4, 6, 12] as const;\r\n const defaultColumns = (() => {\r\n if (isBelow('md')) return 1;\r\n if (isBelow('lg')) return 2;\r\n return 4; // lg, xl, 2xl\r\n })();\r\n // Ensure columns is one of allowed values\r\n const gridColumns = (allowedColumns.includes(columns as any)\r\n ? columns\r\n : defaultColumns) as 1 | 2 | 3 | 4 | 6 | 12;\r\n\r\n // Filter and sort products\r\n const filteredProducts = useMemo(() => {\r\n let result = products;\r\n if (debouncedSearch) {\r\n result = result.filter((p) =>\r\n p.name.toLowerCase().includes(debouncedSearch.toLowerCase())\r\n );\r\n }\r\n if (filter !== 'all') {\r\n result = result.filter((p) =>\r\n p.colors?.includes(filter)\r\n );\r\n }\r\n if (sort === 'price-asc') {\r\n result = [...result].sort((a, b) => a.price - b.price);\r\n } else if (sort === 'price-desc') {\r\n result = [...result].sort((a, b) => b.price - a.price);\r\n }\r\n // Default: featured (no sort)\r\n return result;\r\n }, [products, debouncedSearch, filter, sort]);\r\n\r\n // Collect all colors for filter options\r\n const allColors = useMemo(() => {\r\n const colorSet = new Set<string>();\r\n products.forEach((p) => p.colors?.forEach((c) => colorSet.add(c)));\r\n return Array.from(colorSet);\r\n }, [products]);\r\n\r\n return (\r\n <PageLayout variant=\"centered\" maxWidth=\"xl\" className={cn(className)}>\r\n <PageHeader>\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"flex flex-col md:flex-row md:items-center md:justify-between gap-2 md:gap-4 h-auto md:h-16\">\r\n {/* <span className=\"font-bold text-xl\">Marketplace</span> */}\r\n <div className=\"flex flex-col sm:flex-row gap-2 sm:gap-4 w-full md:w-auto\">\r\n {enableSearch && (\r\n <Input\r\n placeholder=\"Search products...\"\r\n value={search}\r\n onChange={(e) => setSearch(e.target.value)}\r\n className=\"w-full sm:w-64\"\r\n />\r\n )}\r\n {enableFilter && (\r\n <Select\r\n value={filter}\r\n onChange={e => setFilter(e.target.value)}\r\n options={[\r\n { value: 'all', label: 'All Colors' },\r\n ...allColors.map((c) => ({ value: c, label: c })),\r\n ]}\r\n className=\"w-full sm:w-32\"\r\n />\r\n )}\r\n {enableSort && (\r\n <Select\r\n value={sort}\r\n onChange={e => setSort(e.target.value)}\r\n options={[\r\n { value: 'featured', label: 'Featured' },\r\n { value: 'price-asc', label: 'Price: Low to High' },\r\n { value: 'price-desc', label: 'Price: High to Low' },\r\n ]}\r\n className=\"w-full sm:w-40\"\r\n />\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </PageHeader>\r\n\r\n <PageLayoutContent layout=\"centered\" spacing=\"lg\">\r\n <DashboardGrid\r\n columns={columns}\r\n itemMinWidth={itemMinWidth}\r\n itemMaxWidth={itemMaxWidth}\r\n >\r\n {filteredProducts.length === 0 ? (\r\n <div className=\"col-span-full text-center text-gray-500 py-12\">\r\n No products found.\r\n </div>\r\n ) : (\r\n filteredProducts.map((product) => (\r\n <ProductCard\r\n key={product.id}\r\n product={product}\r\n onClick={() => onProductClick && onProductClick(product.id)}\r\n onAddToCart={() => onAddToCart && onAddToCart(product.id)}\r\n onWishlist={() => onWishlist && onWishlist(product.id)}\r\n onShare={() => onShare && onShare(product.id)}\r\n />\r\n ))\r\n )}\r\n </DashboardGrid>\r\n </PageLayoutContent>\r\n </PageLayout>\r\n );\r\n};"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AllProductsView.js","sources":["../../../src/components/AllProductsView/AllProductsView.tsx"],"sourcesContent":["import React, { useState, useMemo } from 'react';\r\nimport { PageLayout, PageHeader, PageLayoutContent } from '../PageLayout';\r\nimport { Input } from '../Input';\r\nimport { Select } from '../Select';\r\nimport { useDebounce } from '../../hooks/useDebounce';\r\nimport { useBreakpoint } from '../../hooks/useBreakpoint';\r\nimport { cn } from '../../utils/cn';\r\nimport { ProductData } from '../SingleProductView/SingleProductView';\r\nimport { ProductCard } from './ProductCard';\r\nimport { DashboardGrid } from '../DashboardGrid/DashboardGrid';\r\n\r\nexport interface AllProductsViewProps {\r\n products: ProductData[];\r\n onProductClick?: (productId: string) => void;\r\n onAddToCart?: (productId: string) => void;\r\n onWishlist?: (productId: string) => void;\r\n onShare?: (productId: string) => void;\r\n enableSearch?: boolean;\r\n enableFilter?: boolean;\r\n enableSort?: boolean;\r\n className?: string;\r\n}\r\n\r\nexport const AllProductsView: React.FC<AllProductsViewProps> = ({\r\n products,\r\n onProductClick,\r\n onAddToCart,\r\n onWishlist,\r\n onShare,\r\n enableSearch = true,\r\n enableFilter = true,\r\n enableSort = true,\r\n className,\r\n}) => {\r\n const [search, setSearch] = useState('');\r\n const debouncedSearch = useDebounce(search, 300);\r\n const [sort, setSort] = useState('featured');\r\n const [filter, setFilter] = useState('all');\r\n const { currentBreakpoint, isBelow } = useBreakpoint();\r\n const isMobile = isBelow('md');\r\n\r\n // Filter and sort products\r\n const filteredProducts = useMemo(() => {\r\n let result = products;\r\n if (debouncedSearch) {\r\n result = result.filter((p) =>\r\n p.name.toLowerCase().includes(debouncedSearch.toLowerCase())\r\n );\r\n }\r\n if (filter !== 'all') {\r\n result = result.filter((p) =>\r\n p.colors?.includes(filter)\r\n );\r\n }\r\n if (sort === 'price-asc') {\r\n result = [...result].sort((a, b) => a.price - b.price);\r\n } else if (sort === 'price-desc') {\r\n result = [...result].sort((a, b) => b.price - a.price);\r\n }\r\n // Default: featured (no sort)\r\n return result;\r\n }, [products, debouncedSearch, filter, sort]);\r\n\r\n // Collect all colors for filter options\r\n const allColors = useMemo(() => {\r\n const colorSet = new Set<string>();\r\n products.forEach((p) => p.colors?.forEach((c) => colorSet.add(c)));\r\n return Array.from(colorSet);\r\n }, [products]);\r\n\r\n return (\r\n <PageLayout variant=\"centered\" maxWidth=\"xl\" className={cn(className)}>\r\n <PageHeader>\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"flex flex-col md:flex-row md:items-center md:justify-between gap-2 md:gap-4 h-auto md:h-16\">\r\n {/* <span className=\"font-bold text-xl\">Marketplace</span> */}\r\n <div className=\"flex flex-col sm:flex-row gap-2 sm:gap-4 w-full md:w-auto\">\r\n {enableSearch && (\r\n <Input\r\n placeholder=\"Search products...\"\r\n value={search}\r\n onChange={(e) => setSearch(e.target.value)}\r\n className=\"w-full sm:w-64\"\r\n />\r\n )}\r\n {enableFilter && (\r\n <Select\r\n value={filter}\r\n onChange={e => setFilter(e.target.value)}\r\n options={[\r\n { value: 'all', label: 'All Colors' },\r\n ...allColors.map((c) => ({ value: c, label: c })),\r\n ]}\r\n className=\"w-full sm:w-32\"\r\n />\r\n )}\r\n {enableSort && (\r\n <Select\r\n value={sort}\r\n onChange={e => setSort(e.target.value)}\r\n options={[\r\n { value: 'featured', label: 'Featured' },\r\n { value: 'price-asc', label: 'Price: Low to High' },\r\n { value: 'price-desc', label: 'Price: High to Low' },\r\n ]}\r\n className=\"w-full sm:w-40\"\r\n />\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </PageHeader>\r\n\r\n <PageLayoutContent layout=\"centered\" spacing=\"lg\">\r\n <DashboardGrid columns={3}>\r\n {filteredProducts.length === 0 ? (\r\n <div className=\"col-span-full text-center text-gray-500 py-12\">\r\n No products found.\r\n </div>\r\n ) : (\r\n filteredProducts.map((product) => (\r\n <ProductCard\r\n key={product.id}\r\n product={product}\r\n onClick={() => onProductClick && onProductClick(product.id)}\r\n onAddToCart={() => onAddToCart && onAddToCart(product.id)}\r\n onWishlist={() => onWishlist && onWishlist(product.id)}\r\n onShare={() => onShare && onShare(product.id)}\r\n />\r\n ))\r\n )}\r\n </DashboardGrid>\r\n </PageLayoutContent>\r\n </PageLayout>\r\n );\r\n};"],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;;;;;;;;;AAuBO,MAAM,eAAe,GAAmC,CAAC,EAC9D,QAAQ,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,OAAO,EACP,YAAY,GAAG,IAAI,EACnB,YAAY,GAAG,IAAI,EACnB,UAAU,GAAG,IAAI,EACjB,SAAS,GACV,KAAI;IACH,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;IACxC,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC;IAChD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC;IAC5C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC3C,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE;AACtD,IAAiB,OAAO,CAAC,IAAI;;AAG7B,IAAA,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAK;QACpC,IAAI,MAAM,GAAG,QAAQ;QACrB,IAAI,eAAe,EAAE;YACnB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KACvB,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,CAC7D;QACH;AACA,QAAA,IAAI,MAAM,KAAK,KAAK,EAAE;AACpB,YAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KACvB,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAC3B;QACH;AACA,QAAA,IAAI,IAAI,KAAK,WAAW,EAAE;YACxB,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QACxD;AAAO,aAAA,IAAI,IAAI,KAAK,YAAY,EAAE;YAChC,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QACxD;;AAEA,QAAA,OAAO,MAAM;IACf,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;;AAG7C,IAAA,MAAM,SAAS,GAAG,OAAO,CAAC,MAAK;AAC7B,QAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU;QAClC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC7B,IAAA,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IAEd,QACEA,KAAC,UAAU,EAAA,EAAC,OAAO,EAAC,UAAU,EAAC,QAAQ,EAAC,IAAI,EAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAA,QAAA,EAAA,CACnEC,GAAA,CAAC,UAAU,EAAA,EAAA,QAAA,EACTA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,wCAAwC,YACrDA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,4FAA4F,EAAA,QAAA,EAEzGD,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,2DAA2D,aACvE,YAAY,KACXC,GAAA,CAAC,KAAK,IACJ,WAAW,EAAC,oBAAoB,EAChC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC1C,SAAS,EAAC,gBAAgB,EAAA,CAC1B,CACH,EACA,YAAY,KACXA,GAAA,CAAC,MAAM,IACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACxC,OAAO,EAAE;AACP,wCAAA,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE;wCACrC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AAClD,qCAAA,EACD,SAAS,EAAC,gBAAgB,EAAA,CAC1B,CACH,EACA,UAAU,KACTA,GAAA,CAAC,MAAM,EAAA,EACL,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACtC,OAAO,EAAE;AACP,wCAAA,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;AACxC,wCAAA,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,oBAAoB,EAAE;AACnD,wCAAA,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,oBAAoB,EAAE;AACrD,qCAAA,EACD,SAAS,EAAC,gBAAgB,EAAA,CAC1B,CACH,CAAA,EAAA,CACG,EAAA,CACF,EAAA,CACF,EAAA,CACK,EAEbA,GAAA,CAAC,iBAAiB,EAAA,EAAC,MAAM,EAAC,UAAU,EAAC,OAAO,EAAC,IAAI,EAAA,QAAA,EAC/CA,GAAA,CAAC,aAAa,EAAA,EAAC,OAAO,EAAE,CAAC,EAAA,QAAA,EACtB,gBAAgB,CAAC,MAAM,KAAK,CAAC,IAC5BA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,+CAA+C,EAAA,QAAA,EAAA,oBAAA,EAAA,CAExD,KAEN,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,MAC3BA,GAAA,CAAC,WAAW,EAAA,EAEV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,cAAc,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,EAC3D,WAAW,EAAE,MAAM,WAAW,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,EACzD,UAAU,EAAE,MAAM,UAAU,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,EACtD,OAAO,EAAE,MAAM,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAA,EALxC,OAAO,CAAC,EAAE,CAMf,CACH,CAAC,CACH,EAAA,CACa,EAAA,CACE,CAAA,EAAA,CACT;AAEjB;;;;"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { Card
|
|
2
|
+
import { Card } from '../Card/Card.js';
|
|
3
3
|
import { Badge } from '../Badge/Badge.js';
|
|
4
4
|
import { Button } from '../Button/Button.js';
|
|
5
5
|
import { ShoppingCart, Heart, Share2 } from 'lucide-react';
|
|
6
6
|
import { cn } from '../../utils/cn.js';
|
|
7
7
|
|
|
8
|
-
const ProductCard = ({ product, onClick, onAddToCart, onWishlist, onShare, className, }) => (jsxs(Card, { className: cn('
|
|
8
|
+
const ProductCard = ({ product, onClick, onAddToCart, onWishlist, onShare, className, }) => (jsxs(Card, { className: cn('p-4 flex flex-col h-full w-full min-w-0 transition-all',
|
|
9
|
+
// Remove max-w-* to allow grid to control width, and add shadow/rounded for separation
|
|
10
|
+
'shadow-md rounded-lg', className), children: [jsx("div", { className: "aspect-square bg-gray-100 rounded-lg mb-4 flex items-center justify-center cursor-pointer w-full", onClick: onClick, title: product.name, children: product.images && product.images[0] ? (jsx("img", { src: product.images[0], alt: product.name, className: "object-contain w-full h-full max-h-32 max-w-full" })) : (jsx(ShoppingCart, { className: "h-12 w-12 text-gray-400" })) }), jsxs("div", { className: "flex items-center space-x-2 mb-2", children: [jsx(Badge, { variant: product.inStock ? 'success' : 'danger', children: product.inStock ? 'In Stock' : 'Out of Stock' }), product.discount && (jsx(Badge, { variant: "danger", children: product.discount }))] }), jsx("h2", { className: "text-lg font-bold text-gray-900 mb-1 truncate", children: product.name }), jsx("p", { className: "text-gray-600 text-sm mb-2 line-clamp-2 break-words", children: product.description }), jsxs("div", { className: "flex items-center space-x-2 mb-4", children: [jsxs("span", { className: "text-xl font-bold text-gray-900", children: ["$", product.price.toFixed(2)] }), product.oldPrice && (jsxs("span", { className: "text-sm text-gray-500 line-through", children: ["$", product.oldPrice.toFixed(2)] }))] }), jsxs("div", { className: "mt-auto flex flex-col sm:flex-row gap-2", children: [jsxs(Button, { variant: "primary", size: "sm", className: "flex-1", onClick: onAddToCart, disabled: !product.inStock, children: [jsx(ShoppingCart, { className: "mr-1 h-4 w-4" }), "Add"] }), jsx(Button, { variant: "outline", size: "sm", onClick: onWishlist, "aria-label": "Wishlist", children: jsx(Heart, { className: "h-4 w-4" }) }), jsx(Button, { variant: "outline", size: "sm", onClick: onShare, "aria-label": "Share", children: jsx(Share2, { className: "h-4 w-4" }) })] })] }));
|
|
9
11
|
|
|
10
12
|
export { ProductCard };
|
|
11
13
|
//# sourceMappingURL=ProductCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductCard.js","sources":["../../../src/components/AllProductsView/ProductCard.tsx"],"sourcesContent":["import React from 'react';\r\nimport {
|
|
1
|
+
{"version":3,"file":"ProductCard.js","sources":["../../../src/components/AllProductsView/ProductCard.tsx"],"sourcesContent":["import React from 'react';\r\nimport { Card } from '../Card';\r\nimport { Badge } from '../Badge';\r\nimport { Button } from '../Button';\r\nimport { Heart, ShoppingCart, Share2 } from 'lucide-react';\r\nimport { ProductData } from '../SingleProductView/SingleProductView';\r\nimport { cn } from '../../utils/cn';\r\n\r\nexport interface ProductCardProps {\r\n product: ProductData;\r\n onClick?: () => void;\r\n onAddToCart?: () => void;\r\n onWishlist?: () => void;\r\n onShare?: () => void;\r\n className?: string;\r\n}\r\n\r\nexport const ProductCard: React.FC<ProductCardProps> = ({\r\n product,\r\n onClick,\r\n onAddToCart,\r\n onWishlist,\r\n onShare,\r\n className,\r\n}) => (\r\n <Card\r\n className={cn(\r\n 'p-4 flex flex-col h-full w-full min-w-0 transition-all',\r\n // Remove max-w-* to allow grid to control width, and add shadow/rounded for separation\r\n 'shadow-md rounded-lg',\r\n className\r\n )}\r\n >\r\n <div\r\n className=\"aspect-square bg-gray-100 rounded-lg mb-4 flex items-center justify-center cursor-pointer w-full\"\r\n onClick={onClick}\r\n title={product.name}\r\n >\r\n {product.images && product.images[0] ? (\r\n <img\r\n src={product.images[0]}\r\n alt={product.name}\r\n className=\"object-contain w-full h-full max-h-32 max-w-full\"\r\n />\r\n ) : (\r\n <ShoppingCart className=\"h-12 w-12 text-gray-400\" />\r\n )}\r\n </div>\r\n <div className=\"flex items-center space-x-2 mb-2\">\r\n <Badge variant={product.inStock ? 'success' : 'danger'}>\r\n {product.inStock ? 'In Stock' : 'Out of Stock'}\r\n </Badge>\r\n {product.discount && (\r\n <Badge variant=\"danger\">{product.discount}</Badge>\r\n )}\r\n </div>\r\n <h2 className=\"text-lg font-bold text-gray-900 mb-1 truncate\">{product.name}</h2>\r\n <p className=\"text-gray-600 text-sm mb-2 line-clamp-2 break-words\">{product.description}</p>\r\n <div className=\"flex items-center space-x-2 mb-4\">\r\n <span className=\"text-xl font-bold text-gray-900\">${product.price.toFixed(2)}</span>\r\n {product.oldPrice && (\r\n <span className=\"text-sm text-gray-500 line-through\">\r\n ${product.oldPrice.toFixed(2)}\r\n </span>\r\n )}\r\n </div>\r\n <div className=\"mt-auto flex flex-col sm:flex-row gap-2\">\r\n <Button\r\n variant=\"primary\"\r\n size=\"sm\"\r\n className=\"flex-1\"\r\n onClick={onAddToCart}\r\n disabled={!product.inStock}\r\n >\r\n <ShoppingCart className=\"mr-1 h-4 w-4\" />\r\n Add\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={onWishlist}\r\n aria-label=\"Wishlist\"\r\n >\r\n <Heart className=\"h-4 w-4\" />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={onShare}\r\n aria-label=\"Share\"\r\n >\r\n <Share2 className=\"h-4 w-4\" />\r\n </Button>\r\n </div>\r\n </Card>\r\n);"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { cn } from '../../utils/cn.js';
|
|
|
5
5
|
|
|
6
6
|
const cardVariants = cva(
|
|
7
7
|
// Mobile-first responsive, content-fitting, vertical stacking
|
|
8
|
-
"w-full flex flex-col rounded-lg border bg-white text-gray-950 shadow-sm
|
|
8
|
+
"w-full max-w-full flex flex-col rounded-lg border bg-white text-gray-950 shadow-sm", {
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: "border-gray-200",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Card.js","sources":["../../../src/components/Card/Card.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\n\nconst cardVariants = cva(\n // Mobile-first responsive, content-fitting, vertical stacking\n \"w-full flex flex-col rounded-lg border bg-white text-gray-950 shadow-sm
|
|
1
|
+
{"version":3,"file":"Card.js","sources":["../../../src/components/Card/Card.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport { cn } from \"../../utils/cn\";\r\n\r\nconst cardVariants = cva(\r\n // Mobile-first responsive, content-fitting, vertical stacking\r\n \"w-full max-w-full flex flex-col rounded-lg border bg-white text-gray-950 shadow-sm\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"border-gray-200\",\r\n elevated: \"border-gray-200 shadow-md\",\r\n outlined: \"border-2 border-primary-200\",\r\n },\r\n padding: {\r\n none: \"p-0\",\r\n sm: \"p-2 sm:p-4\",\r\n md: \"p-3 sm:p-6\",\r\n lg: \"p-4 sm:p-8\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n padding: \"md\",\r\n },\r\n }\r\n);\r\n\r\nexport interface CardProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof cardVariants> {}\r\n\r\nconst Card = React.forwardRef<HTMLDivElement, CardProps>(\r\n ({ className, variant, padding, ...props }, ref) => (\r\n <div\r\n ref={ref}\r\n className={cn(cardVariants({ variant, padding, className }))}\r\n {...props}\r\n />\r\n )\r\n);\r\nCard.displayName = \"Card\";\r\n\r\nconst CardHeader = React.forwardRef<\r\n HTMLDivElement,\r\n React.HTMLAttributes<HTMLDivElement>\r\n>(({ className, ...props }, ref) => (\r\n <div\r\n ref={ref}\r\n className={cn(\"flex flex-col space-y-1.5 p-2 sm:p-4 md:p-6\", className)}\r\n {...props}\r\n />\r\n));\r\nCardHeader.displayName = \"CardHeader\";\r\n\r\nconst CardTitle = React.forwardRef<\r\n HTMLParagraphElement,\r\n React.HTMLAttributes<HTMLHeadingElement>\r\n>(({ className, ...props }, ref) => (\r\n <h3\r\n ref={ref}\r\n className={cn(\r\n \"text-2xl font-semibold leading-none tracking-tight\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n));\r\nCardTitle.displayName = \"CardTitle\";\r\n\r\nconst CardDescription = React.forwardRef<\r\n HTMLParagraphElement,\r\n React.HTMLAttributes<HTMLParagraphElement>\r\n>(({ className, ...props }, ref) => (\r\n <p\r\n ref={ref}\r\n className={cn(\"text-sm text-gray-500\", className)}\r\n {...props}\r\n />\r\n));\r\nCardDescription.displayName = \"CardDescription\";\r\n\r\nconst CardContent = React.forwardRef<\r\n HTMLDivElement,\r\n React.HTMLAttributes<HTMLDivElement>\r\n>(({ className, ...props }, ref) => (\r\n <div ref={ref} className={cn(\"p-2 sm:p-4 md:p-6 pt-0\", className)} {...props} />\r\n));\r\nCardContent.displayName = \"CardContent\";\r\n\r\nconst CardFooter = React.forwardRef<\r\n HTMLDivElement,\r\n React.HTMLAttributes<HTMLDivElement>\r\n>(({ className, ...props }, ref) => (\r\n <div\r\n ref={ref}\r\n className={cn(\"flex items-center p-2 sm:p-4 md:p-6 pt-0\", className)}\r\n {...props}\r\n />\r\n));\r\nCardFooter.displayName = \"CardFooter\";\r\n\r\nexport {\r\n Card,\r\n CardHeader,\r\n CardFooter,\r\n CardTitle,\r\n CardDescription,\r\n CardContent,\r\n cardVariants,\r\n};"],"names":["_jsx"],"mappings":";;;;;AAIA,MAAM,YAAY,GAAG,GAAG;AACtB;AACA,oFAAoF,EACpF;AACE,IAAA,QAAQ,EAAE;AACR,QAAA,OAAO,EAAE;AACP,YAAA,OAAO,EAAE,iBAAiB;AAC1B,YAAA,QAAQ,EAAE,2BAA2B;AACrC,YAAA,QAAQ,EAAE,6BAA6B;AACxC,SAAA;AACD,QAAA,OAAO,EAAE;AACP,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,EAAE,EAAE,YAAY;AAChB,YAAA,EAAE,EAAE,YAAY;AAChB,YAAA,EAAE,EAAE,YAAY;AACjB,SAAA;AACF,KAAA;AACD,IAAA,eAAe,EAAE;AACf,QAAA,OAAO,EAAE,SAAS;AAClB,QAAA,OAAO,EAAE,IAAI;AACd,KAAA;AACF,CAAA;AAOH,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAC3B,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,MAC7CA,GAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,KACxD,KAAK,EAAA,CACT,CACH;AAEH,IAAI,CAAC,WAAW,GAAG,MAAM;AAEzB,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAGjC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,MAC7BA,GAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,6CAA6C,EAAE,SAAS,CAAC,EAAA,GACnE,KAAK,EAAA,CACT,CACH;AACD,UAAU,CAAC,WAAW,GAAG,YAAY;AAErC,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAGhC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,MAC7BA,GAAA,CAAA,IAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CACX,oDAAoD,EACpD,SAAS,CACV,EAAA,GACG,KAAK,EAAA,CACT,CACH;AACD,SAAS,CAAC,WAAW,GAAG,WAAW;AAEnC,MAAM,eAAe,GAAG,KAAK,CAAC,UAAU,CAGtC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,MAC7BA,GAAA,CAAA,GAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,EAAA,GAC7C,KAAK,EAAA,CACT,CACH;AACD,eAAe,CAAC,WAAW,GAAG,iBAAiB;AAE/C,MAAM,WAAW,GAAG,KAAK,CAAC,UAAU,CAGlC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,MAC7BA,GAAA,CAAA,KAAA,EAAA,EAAK,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,wBAAwB,EAAE,SAAS,CAAC,EAAA,GAAM,KAAK,EAAA,CAAI,CACjF;AACD,WAAW,CAAC,WAAW,GAAG,aAAa;AAEvC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAGjC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,MAC7BA,GAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,0CAA0C,EAAE,SAAS,CAAC,EAAA,GAChE,KAAK,EAAA,CACT,CACH;AACD,UAAU,CAAC,WAAW,GAAG,YAAY;;;;"}
|
|
@@ -1,32 +1,13 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
|
-
/**
|
|
4
|
-
* DashboardGrid
|
|
5
|
-
* - Responsive grid layout for dashboard/product cards.
|
|
6
|
-
* - Does not set overflow on its own; relies on children (e.g., Card) to handle overflow.
|
|
7
|
-
* - All Card components now use overflow-hidden by default, preventing content overflow in grid layouts.
|
|
8
|
-
* - Theme-agnostic and reusable.
|
|
9
|
-
*/
|
|
10
3
|
declare const gridVariants: (props?: ({
|
|
11
|
-
columns?:
|
|
4
|
+
columns?: 1 | 2 | 3 | 12 | 4 | 6 | null | undefined;
|
|
12
5
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
13
6
|
declare const gridItemVariants: (props?: ({
|
|
14
|
-
colSpan?: 1 | 2 | 3 |
|
|
7
|
+
colSpan?: 1 | 2 | 3 | 12 | 4 | 6 | null | undefined;
|
|
15
8
|
rowSpan?: 1 | 2 | 3 | 4 | null | undefined;
|
|
16
9
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
17
|
-
interface DashboardGridProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
18
|
-
/**
|
|
19
|
-
* Optional min width for grid items (e.g., "220px" or Tailwind class "min-w-[220px]")
|
|
20
|
-
*/
|
|
21
|
-
itemMinWidth?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Optional max width for grid items (e.g., "320px" or Tailwind class "max-w-[320px]")
|
|
24
|
-
*/
|
|
25
|
-
itemMaxWidth?: string;
|
|
26
|
-
/**
|
|
27
|
-
* Number of columns (fixed) or "auto" for responsive grid
|
|
28
|
-
*/
|
|
29
|
-
columns?: number | "auto";
|
|
10
|
+
interface DashboardGridProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof gridVariants> {
|
|
30
11
|
}
|
|
31
12
|
interface DashboardGridItemProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof gridItemVariants> {
|
|
32
13
|
}
|
|
@@ -3,21 +3,19 @@ import * as React from 'react';
|
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
import { cn } from '../../utils/cn.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* DashboardGrid
|
|
8
|
-
* - Responsive grid layout for dashboard/product cards.
|
|
9
|
-
* - Does not set overflow on its own; relies on children (e.g., Card) to handle overflow.
|
|
10
|
-
* - All Card components now use overflow-hidden by default, preventing content overflow in grid layouts.
|
|
11
|
-
* - Theme-agnostic and reusable.
|
|
12
|
-
*/
|
|
13
|
-
const gridVariants = cva("grid gap-6 w-full", {
|
|
6
|
+
const gridVariants = cva("grid gap-6", {
|
|
14
7
|
variants: {
|
|
15
8
|
columns: {
|
|
16
|
-
|
|
9
|
+
1: "grid-cols-1",
|
|
10
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
11
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
|
12
|
+
4: "grid-cols-1 md:grid-cols-2 lg:grid-cols-4",
|
|
13
|
+
6: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6",
|
|
14
|
+
12: "grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-12",
|
|
17
15
|
},
|
|
18
16
|
},
|
|
19
17
|
defaultVariants: {
|
|
20
|
-
columns:
|
|
18
|
+
columns: 12,
|
|
21
19
|
},
|
|
22
20
|
});
|
|
23
21
|
const gridItemVariants = cva("min-h-0", // Prevents grid items from growing unnecessarily
|
|
@@ -43,44 +41,8 @@ const gridItemVariants = cva("min-h-0", // Prevents grid items from growing unne
|
|
|
43
41
|
rowSpan: 1,
|
|
44
42
|
},
|
|
45
43
|
});
|
|
46
|
-
const DashboardGrid = React.forwardRef(({ className,
|
|
47
|
-
|
|
48
|
-
const itemStyle = {};
|
|
49
|
-
if (itemMinWidth)
|
|
50
|
-
itemStyle.minWidth = itemMinWidth.includes('[') ? undefined : itemMinWidth;
|
|
51
|
-
if (itemMaxWidth)
|
|
52
|
-
itemStyle.maxWidth = itemMaxWidth.includes('[') ? undefined : itemMaxWidth;
|
|
53
|
-
// If Tailwind classes are provided, use them; otherwise, use inline style
|
|
54
|
-
const itemClass = [
|
|
55
|
-
itemMinWidth && itemMinWidth.includes('[') ? `min-w-[${itemMinWidth.replace(/[^0-9a-zA-Z]/g, '')}]` : '',
|
|
56
|
-
itemMaxWidth && itemMaxWidth.includes('[') ? `max-w-[${itemMaxWidth.replace(/[^0-9a-zA-Z]/g, '')}]` : '',
|
|
57
|
-
].filter(Boolean).join(' ');
|
|
58
|
-
// Wrap each child with sizing constraints
|
|
59
|
-
const wrappedChildren = React.Children.map(children, child => jsx("div", { className: itemClass, style: itemStyle, children: child }));
|
|
60
|
-
// If columns is a number, use grid-cols-* Tailwind class
|
|
61
|
-
let gridClass = "grid gap-6 w-full";
|
|
62
|
-
let gridStyle = {};
|
|
63
|
-
const gridColsClassMap = {
|
|
64
|
-
1: "grid-cols-1",
|
|
65
|
-
2: "grid-cols-2",
|
|
66
|
-
3: "grid-cols-3",
|
|
67
|
-
4: "grid-cols-4",
|
|
68
|
-
5: "grid-cols-5",
|
|
69
|
-
6: "grid-cols-6",
|
|
70
|
-
7: "grid-cols-7",
|
|
71
|
-
8: "grid-cols-8",
|
|
72
|
-
9: "grid-cols-9",
|
|
73
|
-
10: "grid-cols-10",
|
|
74
|
-
11: "grid-cols-11",
|
|
75
|
-
12: "grid-cols-12",
|
|
76
|
-
};
|
|
77
|
-
if (typeof columns === "number" && gridColsClassMap[columns]) {
|
|
78
|
-
gridClass += ` ${gridColsClassMap[columns]}`;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
gridStyle.gridTemplateColumns = `repeat(auto-fit, minmax(${itemMinWidth || "220px"}, 1fr))`;
|
|
82
|
-
}
|
|
83
|
-
return (jsx("div", { ref: ref, className: cn(gridClass, className), style: gridStyle, ...props, children: wrappedChildren }));
|
|
44
|
+
const DashboardGrid = React.forwardRef(({ className, columns, ...props }, ref) => {
|
|
45
|
+
return (jsx("div", { ref: ref, className: cn(gridVariants({ columns }), className), ...props }));
|
|
84
46
|
});
|
|
85
47
|
const DashboardGridItem = React.forwardRef(({ className, colSpan, rowSpan, ...props }, ref) => {
|
|
86
48
|
return (jsx("div", { ref: ref, className: cn(gridItemVariants({ colSpan, rowSpan }), className), ...props }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardGrid.js","sources":["../../../src/components/DashboardGrid/DashboardGrid.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\
|
|
1
|
+
{"version":3,"file":"DashboardGrid.js","sources":["../../../src/components/DashboardGrid/DashboardGrid.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport { cn } from \"../../utils/cn\";\r\n\r\nconst gridVariants = cva(\r\n \"grid gap-6\",\r\n {\r\n variants: {\r\n columns: {\r\n 1: \"grid-cols-1\",\r\n 2: \"grid-cols-1 md:grid-cols-2\",\r\n 3: \"grid-cols-1 md:grid-cols-2 lg:grid-cols-3\",\r\n 4: \"grid-cols-1 md:grid-cols-2 lg:grid-cols-4\",\r\n 6: \"grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\",\r\n 12: \"grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-12\",\r\n },\r\n },\r\n defaultVariants: {\r\n columns: 12,\r\n },\r\n }\r\n);\r\n\r\nconst gridItemVariants = cva(\r\n \"min-h-0\", // Prevents grid items from growing unnecessarily\r\n {\r\n variants: {\r\n colSpan: {\r\n 1: \"col-span-1\",\r\n 2: \"col-span-1 md:col-span-2\",\r\n 3: \"col-span-1 md:col-span-2 lg:col-span-3\",\r\n 4: \"col-span-1 md:col-span-2 lg:col-span-4\",\r\n 6: \"col-span-1 md:col-span-2 lg:col-span-3 xl:col-span-6\",\r\n 12: \"col-span-full\",\r\n },\r\n rowSpan: {\r\n 1: \"row-span-1\",\r\n 2: \"row-span-2\",\r\n 3: \"row-span-3\",\r\n 4: \"row-span-4\",\r\n },\r\n },\r\n defaultVariants: {\r\n colSpan: 1,\r\n rowSpan: 1,\r\n },\r\n }\r\n);\r\n\r\ninterface DashboardGridProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof gridVariants> {}\r\n\r\ninterface DashboardGridItemProps\r\n extends React.HTMLAttributes<HTMLDivElement>,\r\n VariantProps<typeof gridItemVariants> {}\r\n\r\nconst DashboardGrid = React.forwardRef<HTMLDivElement, DashboardGridProps>(\r\n ({ className, columns, ...props }, ref) => {\r\n return (\r\n <div\r\n ref={ref}\r\n className={cn(gridVariants({ columns }), className)}\r\n {...props}\r\n />\r\n );\r\n }\r\n);\r\n\r\nconst DashboardGridItem = React.forwardRef<HTMLDivElement, DashboardGridItemProps>(\r\n ({ className, colSpan, rowSpan, ...props }, ref) => {\r\n return (\r\n <div\r\n ref={ref}\r\n className={cn(gridItemVariants({ colSpan, rowSpan }), className)}\r\n {...props}\r\n />\r\n );\r\n }\r\n);\r\n\r\nDashboardGrid.displayName = \"DashboardGrid\";\r\nDashboardGridItem.displayName = \"DashboardGridItem\";\r\n\r\nexport { \r\n DashboardGrid, \r\n DashboardGridItem, \r\n gridVariants, \r\n gridItemVariants \r\n};"],"names":["_jsx"],"mappings":";;;;;AAIA,MAAM,YAAY,GAAG,GAAG,CACtB,YAAY,EACZ;AACE,IAAA,QAAQ,EAAE;AACR,QAAA,OAAO,EAAE;AACP,YAAA,CAAC,EAAE,aAAa;AAChB,YAAA,CAAC,EAAE,4BAA4B;AAC/B,YAAA,CAAC,EAAE,2CAA2C;AAC9C,YAAA,CAAC,EAAE,2CAA2C;AAC9C,YAAA,CAAC,EAAE,0DAA0D;AAC7D,YAAA,EAAE,EAAE,2EAA2E;AAChF,SAAA;AACF,KAAA;AACD,IAAA,eAAe,EAAE;AACf,QAAA,OAAO,EAAE,EAAE;AACZ,KAAA;AACF,CAAA;AAGH,MAAM,gBAAgB,GAAG,GAAG,CAC1B,SAAS;AACT;AACE,IAAA,QAAQ,EAAE;AACR,QAAA,OAAO,EAAE;AACP,YAAA,CAAC,EAAE,YAAY;AACf,YAAA,CAAC,EAAE,0BAA0B;AAC7B,YAAA,CAAC,EAAE,wCAAwC;AAC3C,YAAA,CAAC,EAAE,wCAAwC;AAC3C,YAAA,CAAC,EAAE,sDAAsD;AACzD,YAAA,EAAE,EAAE,eAAe;AACpB,SAAA;AACD,QAAA,OAAO,EAAE;AACP,YAAA,CAAC,EAAE,YAAY;AACf,YAAA,CAAC,EAAE,YAAY;AACf,YAAA,CAAC,EAAE,YAAY;AACf,YAAA,CAAC,EAAE,YAAY;AAChB,SAAA;AACF,KAAA;AACD,IAAA,eAAe,EAAE;AACf,QAAA,OAAO,EAAE,CAAC;AACV,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AACF,CAAA;AAWH,MAAM,aAAa,GAAG,KAAK,CAAC,UAAU,CACpC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,KAAI;IACxC,QACEA,aACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,EAAA,GAC/C,KAAK,EAAA,CACT;AAEN,CAAC;AAGH,MAAM,iBAAiB,GAAG,KAAK,CAAC,UAAU,CACxC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,KAAI;IACjD,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,EAAA,GAC5D,KAAK,EAAA,CACT;AAEN,CAAC;AAGH,aAAa,CAAC,WAAW,GAAG,eAAe;AAC3C,iBAAiB,CAAC,WAAW,GAAG,mBAAmB;;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Product } from './types';
|
|
3
|
+
interface AllProductsViewProps {
|
|
4
|
+
onProductClick?: (product: Product) => void;
|
|
5
|
+
onAddToCart?: (product: Product) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const AllProductsView: React.FC<AllProductsViewProps>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useMemo } from 'react';
|
|
3
|
+
import { Search, Grid2x2, List, Filter, Star, ShoppingCart, Heart, Eye } from 'lucide-react';
|
|
4
|
+
import { Button } from '../Button/Button.js';
|
|
5
|
+
import { Input } from '../Input/Input.js';
|
|
6
|
+
import { Card, CardContent } from '../Card/Card.js';
|
|
7
|
+
import { Badge } from '../Badge/Badge.js';
|
|
8
|
+
import { Modal, ModalHeader, ModalTitle, ModalContent, ModalFooter } from '../Modal/Modal.js';
|
|
9
|
+
import { Checkbox } from '../Checkbox/Checkbox.js';
|
|
10
|
+
import { showToast } from '../Toast/Toast.js';
|
|
11
|
+
import { sampleProducts } from './data/sampleData.js';
|
|
12
|
+
|
|
13
|
+
const sortOptions = [
|
|
14
|
+
{ value: 'relevance', label: 'Most Relevant' },
|
|
15
|
+
{ value: 'price-low', label: 'Price: Low to High' },
|
|
16
|
+
{ value: 'price-high', label: 'Price: High to Low' },
|
|
17
|
+
{ value: 'rating', label: 'Highest Rated' },
|
|
18
|
+
{ value: 'newest', label: 'Newest First' },
|
|
19
|
+
{ value: 'popular', label: 'Most Popular' },
|
|
20
|
+
];
|
|
21
|
+
const AllProductsView = ({ onProductClick, onAddToCart, }) => {
|
|
22
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
23
|
+
const [sortBy, setSortBy] = useState('relevance');
|
|
24
|
+
const [viewMode, setViewMode] = useState('grid');
|
|
25
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
26
|
+
const [showFilters, setShowFilters] = useState(false);
|
|
27
|
+
const [quickViewProduct, setQuickViewProduct] = useState(null);
|
|
28
|
+
const [wishlist, setWishlist] = useState(new Set());
|
|
29
|
+
const [filters, setFilters] = useState({
|
|
30
|
+
categories: [],
|
|
31
|
+
brands: [],
|
|
32
|
+
priceRange: [0, 1000],
|
|
33
|
+
rating: 0,
|
|
34
|
+
inStock: false,
|
|
35
|
+
vendors: [],
|
|
36
|
+
});
|
|
37
|
+
const itemsPerPage = 12;
|
|
38
|
+
// Get unique filter options from products
|
|
39
|
+
const filterOptions = useMemo(() => {
|
|
40
|
+
const categories = [...new Set(sampleProducts.map(p => p.category))];
|
|
41
|
+
const brands = [...new Set(sampleProducts.map(p => p.brand))];
|
|
42
|
+
const vendors = [...new Set(sampleProducts.map(p => p.vendor.name))];
|
|
43
|
+
return { categories, brands, vendors };
|
|
44
|
+
}, []);
|
|
45
|
+
// Filter and sort products
|
|
46
|
+
const filteredProducts = useMemo(() => {
|
|
47
|
+
let filtered = sampleProducts.filter(product => {
|
|
48
|
+
// Search query
|
|
49
|
+
if (searchQuery && !product.name.toLowerCase().includes(searchQuery.toLowerCase()) &&
|
|
50
|
+
!product.description.toLowerCase().includes(searchQuery.toLowerCase())) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// Category filter
|
|
54
|
+
if (filters.categories.length > 0 && !filters.categories.includes(product.category)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Brand filter
|
|
58
|
+
if (filters.brands.length > 0 && !filters.brands.includes(product.brand)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Price range
|
|
62
|
+
if (product.price < filters.priceRange[0] || product.price > filters.priceRange[1]) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// Rating filter
|
|
66
|
+
if (filters.rating > 0 && product.rating < filters.rating) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
// In stock filter
|
|
70
|
+
if (filters.inStock && !product.inStock) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
// Vendor filter
|
|
74
|
+
if (filters.vendors.length > 0 && !filters.vendors.includes(product.vendor.name)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
// Sort products
|
|
80
|
+
switch (sortBy) {
|
|
81
|
+
case 'price-low':
|
|
82
|
+
filtered.sort((a, b) => a.price - b.price);
|
|
83
|
+
break;
|
|
84
|
+
case 'price-high':
|
|
85
|
+
filtered.sort((a, b) => b.price - a.price);
|
|
86
|
+
break;
|
|
87
|
+
case 'rating':
|
|
88
|
+
filtered.sort((a, b) => b.rating - a.rating);
|
|
89
|
+
break;
|
|
90
|
+
case 'newest':
|
|
91
|
+
// Assuming newer products have higher IDs
|
|
92
|
+
filtered.sort((a, b) => parseInt(b.id) - parseInt(a.id));
|
|
93
|
+
break;
|
|
94
|
+
case 'popular':
|
|
95
|
+
filtered.sort((a, b) => b.reviewCount - a.reviewCount);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
return filtered;
|
|
99
|
+
}, [searchQuery, filters, sortBy]);
|
|
100
|
+
// Paginate products
|
|
101
|
+
const paginatedProducts = useMemo(() => {
|
|
102
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
|
103
|
+
return filteredProducts.slice(startIndex, startIndex + itemsPerPage);
|
|
104
|
+
}, [filteredProducts, currentPage]);
|
|
105
|
+
const totalPages = Math.ceil(filteredProducts.length / itemsPerPage);
|
|
106
|
+
const handleFilterChange = (filterType, value) => {
|
|
107
|
+
setFilters(prev => ({
|
|
108
|
+
...prev,
|
|
109
|
+
[filterType]: value,
|
|
110
|
+
}));
|
|
111
|
+
setCurrentPage(1); // Reset to first page when filters change
|
|
112
|
+
};
|
|
113
|
+
const handleClearFilters = () => {
|
|
114
|
+
setFilters({
|
|
115
|
+
categories: [],
|
|
116
|
+
brands: [],
|
|
117
|
+
priceRange: [0, 1000],
|
|
118
|
+
rating: 0,
|
|
119
|
+
inStock: false,
|
|
120
|
+
vendors: [],
|
|
121
|
+
});
|
|
122
|
+
setSearchQuery('');
|
|
123
|
+
setCurrentPage(1);
|
|
124
|
+
};
|
|
125
|
+
const toggleWishlist = (productId) => {
|
|
126
|
+
setWishlist(prev => {
|
|
127
|
+
const newWishlist = new Set(prev);
|
|
128
|
+
if (newWishlist.has(productId)) {
|
|
129
|
+
newWishlist.delete(productId);
|
|
130
|
+
showToast.info('Removed from wishlist');
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
newWishlist.add(productId);
|
|
134
|
+
showToast.success('Added to wishlist');
|
|
135
|
+
}
|
|
136
|
+
return newWishlist;
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
const handleAddToCart = (product) => {
|
|
140
|
+
onAddToCart?.(product);
|
|
141
|
+
showToast.success(`${product.name} added to cart!`);
|
|
142
|
+
};
|
|
143
|
+
const ProductCard = ({ product }) => (jsxs(Card, { className: "group hover:shadow-lg transition-all duration-300", children: [jsxs("div", { className: "relative aspect-square bg-gray-100 rounded-t-lg overflow-hidden", children: [jsx("img", { src: product.images[0], alt: product.name, className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" }), product.discount && (jsxs(Badge, { variant: "danger", className: "absolute top-2 left-2", children: ["-", product.discount, "%"] })), jsx("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
|
|
144
|
+
e.stopPropagation();
|
|
145
|
+
toggleWishlist(product.id);
|
|
146
|
+
}, className: "bg-white/80 hover:bg-white", children: jsx(Heart, { className: `h-4 w-4 ${wishlist.has(product.id) ? 'fill-current text-red-500' : ''}` }) }) }), jsx("div", { className: "absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
|
|
147
|
+
e.stopPropagation();
|
|
148
|
+
setQuickViewProduct(product);
|
|
149
|
+
}, className: "bg-white/80 hover:bg-white", children: jsx(Eye, { className: "h-4 w-4" }) }) }), !product.inStock && (jsx("div", { className: "absolute inset-0 bg-black/50 flex items-center justify-center", children: jsx(Badge, { variant: "secondary", children: "Out of Stock" }) }))] }), jsxs(CardContent, { className: "p-4", children: [jsxs("div", { className: "mb-2", children: [jsx("h3", { className: "font-medium text-gray-900 line-clamp-2 cursor-pointer hover:text-primary-600", onClick: () => onProductClick?.(product), children: product.name }), jsx("p", { className: "text-sm text-gray-600", children: product.brand })] }), jsxs("div", { className: "flex items-center space-x-1 mb-2", children: [jsx(Star, { className: "h-4 w-4 text-yellow-400 fill-current" }), jsxs("span", { className: "text-sm text-gray-600", children: [product.rating, " (", product.reviewCount, ")"] })] }), jsx("div", { className: "flex items-center justify-between mb-3", children: jsxs("div", { className: "flex items-center space-x-2", children: [jsxs("span", { className: "text-lg font-bold text-gray-900", children: ["$", product.price.toFixed(2)] }), product.originalPrice && (jsxs("span", { className: "text-sm text-gray-500 line-through", children: ["$", product.originalPrice.toFixed(2)] }))] }) }), jsxs(Button, { variant: "primary", size: "sm", onClick: (e) => {
|
|
150
|
+
e.stopPropagation();
|
|
151
|
+
handleAddToCart(product);
|
|
152
|
+
}, disabled: !product.inStock, className: "w-full", children: [jsx(ShoppingCart, { className: "mr-2 h-4 w-4" }), "Add to Cart"] })] })] }));
|
|
153
|
+
return (jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8", children: [jsxs("div", { className: "flex flex-col lg:flex-row lg:items-center lg:justify-between mb-8", children: [jsxs("div", { children: [jsx("h1", { className: "text-3xl font-bold text-gray-900 mb-2", children: "All Products" }), jsxs("p", { className: "text-gray-600", children: ["Showing ", filteredProducts.length, " of ", sampleProducts.length, " products"] })] }), jsxs("div", { className: "flex items-center space-x-4 mt-4 lg:mt-0", children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { placeholder: "Search products...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "pl-10 w-64" })] }), jsx("select", { value: sortBy, onChange: (e) => setSortBy(e.target.value), className: "border border-gray-300 rounded-lg px-3 py-2 bg-white", children: sortOptions.map(option => (jsx("option", { value: option.value, children: option.label }, option.value))) }), jsxs("div", { className: "flex items-center bg-gray-100 rounded-lg p-1", children: [jsx(Button, { variant: viewMode === 'grid' ? 'primary' : 'ghost', size: "sm", onClick: () => setViewMode('grid'), children: jsx(Grid2x2, { className: "h-4 w-4" }) }), jsx(Button, { variant: viewMode === 'list' ? 'primary' : 'ghost', size: "sm", onClick: () => setViewMode('list'), children: jsx(List, { className: "h-4 w-4" }) })] }), jsxs(Button, { variant: "outline", onClick: () => setShowFilters(true), className: "lg:hidden", children: [jsx(Filter, { className: "mr-2 h-4 w-4" }), "Filters"] })] })] }), jsxs("div", { className: "flex gap-8", children: [jsx("div", { className: "hidden lg:block w-64 flex-shrink-0", children: jsx(Card, { className: "sticky top-4", children: jsxs(CardContent, { className: "p-6", children: [jsxs("div", { className: "flex items-center justify-between mb-4", children: [jsx("h3", { className: "font-semibold text-gray-900", children: "Filters" }), jsx(Button, { variant: "ghost", size: "sm", onClick: handleClearFilters, children: "Clear All" })] }), jsxs("div", { className: "mb-6", children: [jsx("h4", { className: "font-medium text-gray-900 mb-3", children: "Categories" }), jsx("div", { className: "space-y-2", children: filterOptions.categories.map(category => (jsxs("label", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { checked: filters.categories.includes(category), onChange: (e) => {
|
|
154
|
+
const newCategories = e.target.checked
|
|
155
|
+
? [...filters.categories, category]
|
|
156
|
+
: filters.categories.filter(c => c !== category);
|
|
157
|
+
handleFilterChange('categories', newCategories);
|
|
158
|
+
} }), jsx("span", { className: "text-sm text-gray-700", children: category })] }, category))) })] }), jsxs("div", { className: "mb-6", children: [jsx("h4", { className: "font-medium text-gray-900 mb-3", children: "Brands" }), jsx("div", { className: "space-y-2", children: filterOptions.brands.map(brand => (jsxs("label", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { checked: filters.brands.includes(brand), onChange: (e) => {
|
|
159
|
+
const newBrands = e.target.checked
|
|
160
|
+
? [...filters.brands, brand]
|
|
161
|
+
: filters.brands.filter(b => b !== brand);
|
|
162
|
+
handleFilterChange('brands', newBrands);
|
|
163
|
+
} }), jsx("span", { className: "text-sm text-gray-700", children: brand })] }, brand))) })] }), jsxs("div", { className: "mb-6", children: [jsx("h4", { className: "font-medium text-gray-900 mb-3", children: "Price Range" }), jsx("div", { className: "space-y-2", children: jsxs("div", { className: "flex items-center space-x-2", children: [jsx(Input, { type: "number", placeholder: "Min", value: filters.priceRange[0], onChange: (e) => handleFilterChange('priceRange', [
|
|
164
|
+
parseInt(e.target.value) || 0,
|
|
165
|
+
filters.priceRange[1]
|
|
166
|
+
]), className: "w-20" }), jsx("span", { children: "-" }), jsx(Input, { type: "number", placeholder: "Max", value: filters.priceRange[1], onChange: (e) => handleFilterChange('priceRange', [
|
|
167
|
+
filters.priceRange[0],
|
|
168
|
+
parseInt(e.target.value) || 1000
|
|
169
|
+
]), className: "w-20" })] }) })] }), jsxs("div", { className: "mb-6", children: [jsx("h4", { className: "font-medium text-gray-900 mb-3", children: "Minimum Rating" }), jsx("div", { className: "space-y-2", children: [4, 3, 2, 1].map(rating => (jsxs("label", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { checked: filters.rating === rating, onChange: (e) => {
|
|
170
|
+
handleFilterChange('rating', e.target.checked ? rating : 0);
|
|
171
|
+
} }), jsxs("div", { className: "flex items-center space-x-1", children: [[1, 2, 3, 4, 5].map(star => (jsx(Star, { className: `h-4 w-4 ${star <= rating ? 'text-yellow-400 fill-current' : 'text-gray-300'}` }, star))), jsx("span", { className: "text-sm text-gray-700", children: "& up" })] })] }, rating))) })] }), jsx("div", { className: "mb-6", children: jsxs("label", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { checked: filters.inStock, onChange: (e) => handleFilterChange('inStock', e.target.checked) }), jsx("span", { className: "text-sm text-gray-700", children: "In Stock Only" })] }) })] }) }) }), jsx("div", { className: "flex-1", children: paginatedProducts.length === 0 ? (jsxs("div", { className: "text-center py-12", children: [jsx("div", { className: "text-gray-400 mb-4", children: jsx(Search, { className: "h-16 w-16 mx-auto" }) }), jsx("h3", { className: "text-lg font-medium text-gray-900 mb-2", children: "No products found" }), jsx("p", { className: "text-gray-600 mb-4", children: "Try adjusting your search or filter criteria" }), jsx(Button, { variant: "outline", onClick: handleClearFilters, children: "Clear Filters" })] })) : (jsxs(Fragment, { children: [jsx("div", { className: `grid gap-6 ${viewMode === 'grid'
|
|
172
|
+
? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'
|
|
173
|
+
: 'grid-cols-1'}`, children: paginatedProducts.map(product => (jsx(ProductCard, { product: product }, product.id))) }), totalPages > 1 && (jsxs("div", { className: "flex items-center justify-center space-x-2 mt-12", children: [jsx(Button, { variant: "outline", onClick: () => setCurrentPage(prev => Math.max(1, prev - 1)), disabled: currentPage === 1, children: "Previous" }), Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
|
174
|
+
const page = i + 1;
|
|
175
|
+
return (jsx(Button, { variant: currentPage === page ? 'primary' : 'outline', onClick: () => setCurrentPage(page), children: page }, page));
|
|
176
|
+
}), jsx(Button, { variant: "outline", onClick: () => setCurrentPage(prev => Math.min(totalPages, prev + 1)), disabled: currentPage === totalPages, children: "Next" })] }))] })) })] }), jsxs(Modal, { open: showFilters, onOpenChange: setShowFilters, children: [jsx(ModalHeader, { children: jsx(ModalTitle, { children: "Filters" }) }), jsx(ModalContent, { children: jsx("div", { className: "space-y-6", children: jsxs("div", { children: [jsx("h4", { className: "font-medium text-gray-900 mb-3", children: "Categories" }), jsx("div", { className: "space-y-2", children: filterOptions.categories.map(category => (jsxs("label", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { checked: filters.categories.includes(category), onChange: (e) => {
|
|
177
|
+
const newCategories = e.target.checked
|
|
178
|
+
? [...filters.categories, category]
|
|
179
|
+
: filters.categories.filter(c => c !== category);
|
|
180
|
+
handleFilterChange('categories', newCategories);
|
|
181
|
+
} }), jsx("span", { className: "text-sm text-gray-700", children: category })] }, category))) })] }) }) }), jsxs(ModalFooter, { children: [jsx(Button, { variant: "outline", onClick: handleClearFilters, children: "Clear All" }), jsx(Button, { variant: "primary", onClick: () => setShowFilters(false), children: "Apply Filters" })] })] }), jsx(Modal, { open: !!quickViewProduct, onOpenChange: () => setQuickViewProduct(null), size: "lg", children: quickViewProduct && (jsxs(Fragment, { children: [jsx(ModalHeader, { children: jsx(ModalTitle, { children: quickViewProduct.name }) }), jsx(ModalContent, { children: jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [jsx("div", { className: "aspect-square bg-gray-100 rounded-lg overflow-hidden", children: jsx("img", { src: quickViewProduct.images[0], alt: quickViewProduct.name, className: "w-full h-full object-cover" }) }), jsxs("div", { children: [jsxs("div", { className: "flex items-center space-x-1 mb-2", children: [jsx(Star, { className: "h-4 w-4 text-yellow-400 fill-current" }), jsxs("span", { className: "text-sm text-gray-600", children: [quickViewProduct.rating, " (", quickViewProduct.reviewCount, " reviews)"] })] }), jsxs("div", { className: "flex items-center space-x-2 mb-4", children: [jsxs("span", { className: "text-2xl font-bold text-gray-900", children: ["$", quickViewProduct.price.toFixed(2)] }), quickViewProduct.originalPrice && (jsxs("span", { className: "text-lg text-gray-500 line-through", children: ["$", quickViewProduct.originalPrice.toFixed(2)] }))] }), jsx("p", { className: "text-gray-600 mb-4", children: quickViewProduct.description }), jsx(Badge, { variant: quickViewProduct.inStock ? 'success' : 'danger', className: "mb-4", children: quickViewProduct.inStock ? 'In Stock' : 'Out of Stock' })] })] }) }), jsxs(ModalFooter, { children: [jsx(Button, { variant: "outline", onClick: () => onProductClick?.(quickViewProduct), children: "View Details" }), jsxs(Button, { variant: "primary", onClick: () => handleAddToCart(quickViewProduct), disabled: !quickViewProduct.inStock, children: [jsx(ShoppingCart, { className: "mr-2 h-4 w-4" }), "Add to Cart"] })] })] })) })] }));
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export { AllProductsView };
|
|
185
|
+
//# sourceMappingURL=AllProductsView.js.map
|