Compare commits
10 Commits
6bb1758040
...
ada2e1cb72
| Author | SHA1 | Date | |
|---|---|---|---|
| ada2e1cb72 | |||
| 786c9e13f0 | |||
| 0acdfdb1eb | |||
| 3d49f32175 | |||
| 3be5cba323 | |||
| b7c5734a1b | |||
| a155c991c6 | |||
| b6ffdaf03f | |||
| 325b27262e | |||
| 8771e025bc |
BIN
src/client/dd-hub-react/public/assets/images/dd-button-icon.webp
Normal file
|
After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 149 KiB |
31
src/client/dd-hub-react/src/api/product-service.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export type Product = {
|
||||
id: string;
|
||||
name: string;
|
||||
price?: number;
|
||||
status?: string;
|
||||
version?: string;
|
||||
coverUrl?: string;
|
||||
colors?: string[];
|
||||
priceSale?: number;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* send a request to the server to get the products
|
||||
* @returns Array of products
|
||||
*/
|
||||
export function getProductsAsync(): Promise<Product[]> {
|
||||
//TODO: Implement the API call using fetch or axios
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: '1',
|
||||
name: "User Manager",
|
||||
version: "1.0.0"
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: "Envelope Generator",
|
||||
version: "1.0.0"
|
||||
}
|
||||
]);
|
||||
}
|
||||
@@ -19,11 +19,12 @@ type AppProps = {
|
||||
export default function App({ children }: AppProps) {
|
||||
useScrollToTop();
|
||||
|
||||
const githubButton = () => (
|
||||
const ddButton = () => (
|
||||
<Fab
|
||||
size="medium"
|
||||
aria-label="Github"
|
||||
href="https://github.com/minimal-ui-kit/material-kit-react"
|
||||
aria-label="Digital Data"
|
||||
href="https://digitaldata.works/"
|
||||
target="_blank"
|
||||
sx={{
|
||||
zIndex: 9,
|
||||
right: 20,
|
||||
@@ -31,17 +32,21 @@ export default function App({ children }: AppProps) {
|
||||
width: 48,
|
||||
height: 48,
|
||||
position: 'fixed',
|
||||
bgcolor: 'grey.800',
|
||||
bgcolor: 'transparent',
|
||||
boxShadow: 'none'
|
||||
}}
|
||||
>
|
||||
<Iconify width={24} icon="socials:github" sx={{ '--color': 'white' }} />
|
||||
<img
|
||||
src="/assets/images/dd-button-icon.webp"
|
||||
alt="Digital Data"
|
||||
/>
|
||||
</Fab>
|
||||
);
|
||||
|
||||
return (
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
{githubButton()}
|
||||
{ddButton()}
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -56,4 +61,4 @@ function useScrollToTop() {
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Link from '@mui/material/Link';
|
||||
import Card from '@mui/material/Card';
|
||||
@@ -6,22 +8,15 @@ import Typography from '@mui/material/Typography';
|
||||
|
||||
import { fCurrency } from 'src/utils/format-number';
|
||||
|
||||
import { Product } from 'src/api/product-service';
|
||||
|
||||
import { Label } from 'src/components/label';
|
||||
import { ColorPreview } from 'src/components/color-utils';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export type ProductItemProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
price: number;
|
||||
status: string;
|
||||
coverUrl: string;
|
||||
colors: string[];
|
||||
priceSale: number | null;
|
||||
};
|
||||
|
||||
export function ProductItem({ product }: { product: ProductItemProps }) {
|
||||
// TODO: Add explanation part
|
||||
export function ProductItem({ product }: { product: Product }) {
|
||||
const renderStatus = (
|
||||
<Label
|
||||
variant="inverted"
|
||||
@@ -38,11 +33,30 @@ export function ProductItem({ product }: { product: ProductItemProps }) {
|
||||
</Label>
|
||||
);
|
||||
|
||||
const renderVersion = (
|
||||
<Label
|
||||
variant="inverted"
|
||||
color='info'
|
||||
sx={{
|
||||
zIndex: 9,
|
||||
top: 16,
|
||||
right: 16,
|
||||
position: 'absolute',
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
{product.version}
|
||||
</Label>
|
||||
);
|
||||
|
||||
const [imgSrc, setImgSrc] = useState(product.coverUrl ?? "/assets/images/product/product-default.webp");
|
||||
|
||||
const renderImg = (
|
||||
<Box
|
||||
component="img"
|
||||
alt={product.name}
|
||||
src={product.coverUrl}
|
||||
src={imgSrc}
|
||||
onError={() => setImgSrc('/assets/images/product/product-default.webp')}
|
||||
sx={{
|
||||
top: 0,
|
||||
width: 1,
|
||||
@@ -74,11 +88,12 @@ export function ProductItem({ product }: { product: ProductItemProps }) {
|
||||
<Card>
|
||||
<Box sx={{ pt: '100%', position: 'relative' }}>
|
||||
{product.status && renderStatus}
|
||||
{product.version && renderVersion}
|
||||
{renderImg}
|
||||
</Box>
|
||||
|
||||
<Stack spacing={2} sx={{ p: 3 }}>
|
||||
<Link color="inherit" underline="hover" variant="subtitle2" noWrap>
|
||||
<Link color="inherit" underline="hover" variant="subtitle2" noWrap target="_blank" href={product.url ?? '/404'}>
|
||||
{product.name}
|
||||
</Link>
|
||||
|
||||
@@ -89,7 +104,7 @@ export function ProductItem({ product }: { product: ProductItemProps }) {
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<ColorPreview colors={product.colors} />
|
||||
<ColorPreview colors={product?.colors ?? []} />
|
||||
{renderPrice}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
@@ -7,9 +7,10 @@ import Typography from '@mui/material/Typography';
|
||||
|
||||
import { _products } from 'src/_mock';
|
||||
import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { getProductsAsync, Product } from 'src/api/product-service';
|
||||
|
||||
import { ProductItem } from '../product-item';
|
||||
import { ProductSort } from '../product-sort';
|
||||
import { ProductItem } from '../product-item';
|
||||
import { CartIcon } from '../product-cart-widget';
|
||||
import { ProductFilters } from '../product-filters';
|
||||
|
||||
@@ -64,6 +65,8 @@ export function ProductsView() {
|
||||
|
||||
const [filters, setFilters] = useState<FiltersProps>(defaultFilters);
|
||||
|
||||
const [products, setProducts] = useState<Array<Product>>([]);
|
||||
|
||||
const handleOpenFilter = useCallback(() => {
|
||||
setOpenFilter(true);
|
||||
}, []);
|
||||
@@ -84,6 +87,12 @@ export function ProductsView() {
|
||||
(key) => filters[key as keyof FiltersProps] !== defaultFilters[key as keyof FiltersProps]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getProductsAsync().then((res) => {
|
||||
setProducts(res);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DashboardContent>
|
||||
<CartIcon totalItems={8} />
|
||||
@@ -139,7 +148,7 @@ export function ProductsView() {
|
||||
</Box>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
{_products.map((product) => (
|
||||
{products.map((product) => (
|
||||
<Grid key={product.id} size={{ xs: 12, sm: 6, md: 3 }}>
|
||||
<ProductItem product={product} />
|
||||
</Grid>
|
||||
|
||||