Skip to content

Commit

Permalink
Improve performance and ui
Browse files Browse the repository at this point in the history
  • Loading branch information
minsoeaung committed Dec 24, 2023
1 parent af2cf4b commit 0ec72be
Show file tree
Hide file tree
Showing 17 changed files with 280 additions and 162 deletions.
2 changes: 2 additions & 0 deletions API/API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0" />
<PackageReference Include="AWSSDK.CloudFront" Version="3.7.300.5" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.300" />
<PackageReference Include="AWSSDK.S3" Version="3.7.300.3" />
Expand All @@ -32,6 +33,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.14" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.11" />
<PackageReference Include="Nager.Country" Version="4.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.11" />
Expand Down
1 change: 1 addition & 0 deletions API/Entities/Brand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ namespace API.Entities;
public class Brand
{
public int Id { get; set; }
// TODO: no comma allowed
[Required] public string Name { get; set; }
}
11 changes: 10 additions & 1 deletion API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
using API.Entities;
using API.Extensions;
using API.Services;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
Expand All @@ -15,7 +17,6 @@
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
Expand Down Expand Up @@ -109,6 +110,9 @@

builder.Services.AddMappings();

builder.Services.AddHealthChecks()
.AddDbContextCheck<StoreContext>();

var app = builder.Build();

app.UseExceptionHandler("/error");
Expand Down Expand Up @@ -137,4 +141,9 @@

app.MapFallbackToFile("index.html");

app.MapHealthChecks("/_health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

app.Run();
2 changes: 1 addition & 1 deletion Client/src/components/AddToCartButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const AddToCartButton = ({ isInCart, productId, buttonProps }: Props) =>
variant="outline"
{...buttonProps}
onClick={async (e) => {
e.preventDefault();
e.stopPropagation();

if (user) {
await mutation.mutateAsync({
Expand Down
17 changes: 15 additions & 2 deletions Client/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ const Header = () => {
}
}, [myAccount]);

useEffect(() => {
const searchTerm = searchParams.get('searchTerm');
if (!searchTerm) setSearchInputValue('');
}, [searchParams]);

const search = () => {
searchParams.set('searchTerm', searchInputValue);
setSearchParams(searchParams);
Expand Down Expand Up @@ -109,7 +114,13 @@ const Header = () => {
</Link>
<Flex alignItems="center">
<Stack direction="row" spacing={{ base: 1, md: 2 }} alignItems="center">
<IconButton aria-label="Show search" variant="ghost" icon={<SearchIcon />} onClick={onOpen} />
<IconButton
aria-label="Show search"
variant="ghost"
colorScheme={isOpen ? 'blue' : 'gray'}
icon={<SearchIcon />}
onClick={onOpen}
/>
<IconButton
aria-label="Color mode"
variant="ghost"
Expand All @@ -125,6 +136,7 @@ const Header = () => {
to="/user/wishlist"
aria-label="Wish list"
variant="ghost"
colorScheme={window.location.pathname === '/user/wishlist' ? 'blue' : 'gray'}
icon={<FaRegHeart />}
/>
{Array.isArray(wishList) && wishList.length > 0 && (
Expand Down Expand Up @@ -201,6 +213,7 @@ const Header = () => {
to="/user/cart"
aria-label="Cart"
variant="ghost"
colorScheme={window.location.pathname === '/user/cart' ? 'blue' : 'gray'}
icon={<FiShoppingCart />}
/>
{Array.isArray(cart?.cartItems) && cart!.cartItems.length > 0 && (
Expand Down Expand Up @@ -255,7 +268,7 @@ const Header = () => {
minW={0}
pl={3}
>
<Avatar size={'sm'} src={user?.profilePicture} />
<Avatar size={'sm'} src={user?.profilePicture} border="1px solid #ccc" />
</MenuButton>
<MenuList alignItems={'center'} zIndex={3} maxW="3xs">
<br />
Expand Down
135 changes: 84 additions & 51 deletions Client/src/components/ProductFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {
Box,
Button,
Checkbox,
Heading,
HStack,
Icon,
IconButton,
Modal,
ModalBody,
Expand All @@ -11,16 +12,21 @@ import {
ModalFooter,
ModalHeader,
ModalOverlay,
Tag,
TagLabel,
TagLeftIcon,
useColorModeValue,
useDisclosure,
Wrap,
} from '@chakra-ui/react';
import { AiOutlineFilter } from 'react-icons/ai';
import { useCallback, useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import useProductFilters from '../hooks/queries/useProductFilters.ts';
import { IoMdRadioButtonOff } from 'react-icons/io';
import { CheckCircleIcon } from '@chakra-ui/icons';

export const ProductFilters = () => {
export const ProductFilters = memo(() => {
const [searchParams, setSearchParams] = useSearchParams();
const { isOpen, onOpen, onClose } = useDisclosure();

Expand All @@ -42,31 +48,25 @@ export const ProductFilters = () => {
const catsObj: Record<string, true> = {};
catsArray.forEach((b) => (catsObj[b] = true));
setSelectedCategories(catsObj);
}, []);
}, [isOpen]);

const handleBrandsChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const handleBrandsChange = (name: string) => {
setSelectedBrands((prevState) => {
const d = { ...prevState };
if (event.target.checked) {
d[event.target.name] = true;
} else {
delete d[event.target.name];
}
if (d[name]) delete d[name];
else d[name] = true;
return d;
});
}, []);
};

const handleCategoriesChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const handleCategoriesChange = (name: string) => {
setSelectedCategories((prevState) => {
const d = { ...prevState };
if (event.target.checked) {
d[event.target.name] = true;
} else {
delete d[event.target.name];
}
if (d[name]) delete d[name];
else d[name] = true;
return d;
});
}, []);
};

const handleSave = () => {
searchParams.set('brands', Object.keys(selectedBrands).join(','));
Expand All @@ -75,16 +75,20 @@ export const ProductFilters = () => {
onClose();
};

// const filterApplied = !!Object.keys(selectedCategories).length || !!Object.keys(selectedCategories).length
const handleReset = () => {
setSelectedCategories({});
setSelectedBrands({});
};

const filterApplied = !!Object.keys(selectedCategories).length || !!Object.keys(selectedBrands).length;

return (
<>
<IconButton
aria-label="Filter"
variant="ghost"
size={{ base: 'sm', md: 'lg' }}
// variant={filterApplied ? "solid" : "outline"}
// colorScheme={filterApplied ? "blue" : "gray"}
colorScheme={filterApplied ? 'blue' : 'gray'}
icon={<AiOutlineFilter />}
onClick={onOpen}
isLoading={isLoading}
Expand All @@ -96,50 +100,79 @@ export const ProductFilters = () => {
<ModalCloseButton />
<ModalBody>
<Heading as="h3" size="sm" color={useColorModeValue('red.500', 'red.300')}>
Brands
Filter by brand
</Heading>
<Box overflowY="auto" mt={2}>
<Box overflowY="auto" mt={3}>
<Wrap>
{data &&
data.brands.map((brand) => (
<Checkbox
key={brand.id}
name={brand.name}
onChange={handleBrandsChange}
isChecked={selectedBrands[brand.name]}
>
{brand.name}
</Checkbox>
))}
data.brands.map((brand) => {
const isSelected = selectedBrands[brand.name];

return (
<Tag
key={brand.id}
variant={isSelected ? 'outline' : 'subtle'}
colorScheme={isSelected ? 'messenger' : 'gray'}
onClick={() => handleBrandsChange(brand.name)}
cursor="pointer"
userSelect="none"
>
<TagLeftIcon>
{isSelected ? (
<CheckCircleIcon />
) : (
<Icon as={IoMdRadioButtonOff} fontSize="24px" />
)}
</TagLeftIcon>
<TagLabel>{brand.name}</TagLabel>
</Tag>
);
})}
</Wrap>
</Box>
<Heading as="h3" size="sm" mt={2} color={useColorModeValue('red.500', 'red.300')}>
Categories
<Heading as="h3" size="sm" mt={4} color={useColorModeValue('red.500', 'red.300')}>
Filter by category
</Heading>
<Box overflowY="auto" mt={2}>
<Box overflowY="auto" mt={3}>
<Wrap>
{data &&
data.categories.map((category) => (
<Checkbox
key={category.id}
name={category.name}
onChange={handleCategoriesChange}
isChecked={selectedCategories[category.name]}
>
{category.name}
</Checkbox>
))}
data.categories.map((category) => {
const isSelected = selectedCategories[category.name];

return (
<Tag
key={category.id}
variant={isSelected ? 'outline' : 'subtle'}
colorScheme={isSelected ? 'messenger' : 'gray'}
onClick={() => handleCategoriesChange(category.name)}
cursor="pointer"
userSelect="none"
>
<TagLeftIcon>
{isSelected ? (
<CheckCircleIcon />
) : (
<Icon as={IoMdRadioButtonOff} fontSize="24px" />
)}
</TagLeftIcon>
<TagLabel>{category.name}</TagLabel>
</Tag>
);
})}
</Wrap>
</Box>
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={handleSave}>
Save
</Button>
<Button onClick={onClose}>Close</Button>
<HStack>
<Button onClick={onClose}>Close</Button>
<Button onClick={handleReset}>Reset</Button>
<Button colorScheme="blue" onClick={handleSave}>
Apply
</Button>
</HStack>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
});
5 changes: 3 additions & 2 deletions Client/src/components/ProductSortBy.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons';
import { useSearchParams } from 'react-router-dom';
import { memo } from 'react';

const sortMenus = {
name: 'Name',
Expand All @@ -10,7 +11,7 @@ const sortMenus = {
_: '',
};

export const ProductSortBy = () => {
export const ProductSortBy = memo(() => {
const [params, setParams] = useSearchParams();

const handleSortMenuClick = (value: string) => () => {
Expand Down Expand Up @@ -40,4 +41,4 @@ export const ProductSortBy = () => {
</MenuList>
</Menu>
);
};
});
3 changes: 2 additions & 1 deletion Client/src/components/Products/FavouriteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export const FavouriteButton = ({ isChecked, productId, iconButtonProps }: Props
boxShadow="base"
{...iconButtonProps}
onClick={async (e) => {
e.preventDefault();
e.stopPropagation();

await mutation.mutateAsync({
type: isChecked ? 'REMOVE' : 'ADD',
productId,
Expand Down
Loading

0 comments on commit 0ec72be

Please sign in to comment.