Skip to content

Commit

Permalink
Merge pull request #145 from Arquisoft/develop
Browse files Browse the repository at this point in the history
Develop new features
  • Loading branch information
sebaslh01 authored Apr 29, 2022
2 parents c86bd39 + 347189a commit e34b131
Show file tree
Hide file tree
Showing 17 changed files with 670 additions and 63 deletions.
53 changes: 45 additions & 8 deletions restapi/controller/ProductController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { v4 as uuidv4 } from 'uuid';
const path = require('path');
const fs = require('fs');
const ObjectId = require('mongodb').ObjectID;
const qs = require('qs');






Expand Down Expand Up @@ -42,19 +46,52 @@ class ProductController {
}

public async getProducts(req: Request, res: Response) {
let params = req.query.search;
if(params){
const products = await Product.find({name: {$regex: params, $options: 'i'}});
res.status(200).json(products);
let rating = 0;

let userQuery = qs.parse(req.query);
let query:any = {};
for(let key in userQuery){
if (typeof userQuery[key] === "string"){
userQuery[key] = {eq: userQuery[key]};
}
query[key] = {}
for (let i = 0; i < Object.keys(userQuery[key]).length; i++) {
console.log(query[key]);
Object.assign(query[key], buildQuery(key,Object.keys(userQuery[key])[i], Object.values(userQuery[key])[i] as string));
}
}
else{
var products = await Product.find({});
//products = await productController.addImagePaths(products);
res.send(products);
console.log("Searching products using ", query);
const products = await Product.find(query);
if(rating > 0){
res.status(200).json(products.filter(product => product.reviews.reduce((acc, review) => acc + review.rating, 0) / product.reviews.length >= rating));
return;
}
res.status(200).json(products);


function buildQuery(field:string,comparator:string, value:string):any{
let query:any = {};

if(field == "rating"){
rating = parseInt(value);
}
if(["name","description","color","category","brand"].includes(field)){
return {$regex: value, $options: 'i'};
}
switch(comparator){
case "gte":
return {$gte: value};
case "lte":
return {$lte: value};
case "eq":
return {$eq: value};
default:
return {}
}
}
}


public async getProductsByIds(ids: string[]) {
var products = await Product.find({_id: {$in: ids}});
return products;
Expand Down
9 changes: 9 additions & 0 deletions restapi/model/Product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ const ProductSchema = new Schema({
type: String,
required: true
},
color:{
type: String,
required: true
},
reviews: {
type: Array,
required: true
},
brand:{
type: String,
}


}, {
timestamps: true
});
Expand All @@ -37,6 +45,7 @@ export interface ProductModel extends moongose.Document {
category: string;
reviews: Array<ReviewModel>;
numImages:number;
brand:string;
}

ProductSchema.method('toClient', function() {
Expand Down
1 change: 1 addition & 0 deletions restapi/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions webapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ function App(): JSX.Element {
<>
<ReactNotifications/>

<Header cart={cart} />
<Container style={{ alignContent: "center", marginTop: "5%", minHeight: "50vh" }} maxWidth="lg">
<Router>
<Router>
<Header cart={cart} />
<Routes>
<Route path='/' element={<MainProducts refreshCartList={refreshCartList}/>} />
<Route path="/products/:id" element={<ProductPage refreshCartList={refreshCartList}/>} />
Expand Down
8 changes: 5 additions & 3 deletions webapp/src/components/administrator/AdminView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Autocomplete, Button, TextField, Box } from '@mui/material';

import { addProduct,deleteProduct,getOrders, getProducts } from '../../api/api';
import { Order, Product } from '../../shared/shareddtypes';
import OrderCard from '../user/OrderCard';
import OrderAdminCard from './OrderAdminCard';
import OrdersChart from "./OrdersChart";
import { useNavigate } from 'react-router-dom';
import { Logout } from '@mui/icons-material';
Expand Down Expand Up @@ -154,8 +154,10 @@ function AdminView(): JSX.Element {


<label >All Orders: </label>
{orders.map(order => (
<OrderCard order={order} />
{orders.sort((a, b) => {
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
}).map(order => (
<OrderAdminCard order={order} />
))}
</Stack>
</Box>
Expand Down
76 changes: 76 additions & 0 deletions webapp/src/components/administrator/OrderAdminCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import "../../css/OrderCard.css";

import { styled } from '@mui/material/styles';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import { Order } from "../../shared/shareddtypes";
import OrderAdminDetails from "./OrderAdminDetails";
import React from "react";
import { CardActions, Collapse, IconButton, IconButtonProps } from "@mui/material";
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

type OrderAdminCardProps = {
order: Order;
}

interface ExpandMoreAdminProps extends IconButtonProps {
expand: boolean;
}

const ExpandMoreAdmin = styled((props: ExpandMoreAdminProps) => {
const { expand, ...other } = props;
return <IconButton {...other} />;
})(({ theme, expand }) => ({
transform: !expand ? 'rotate(0deg)' : 'rotate(180deg)',
marginLeft: 'auto',
transition: theme.transitions.create('transform', {
duration: theme.transitions.duration.shortest,
}),
}));

export default function OrderAdminCard(props: OrderAdminCardProps) {

const [expanded, setExpanded] = React.useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};

return (
<Card id="orderAdminCard" variant="outlined">
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Order {props.order.createdAt.toString().substring(0, 10)}
</Typography>
<Typography gutterBottom variant="h5" component="div">
{props.order.userId.toString()}
</Typography>
Items:
<CardActions disableSpacing>
<ExpandMoreAdmin
expand={expanded}
onClick={handleExpandClick}
aria-expanded={expanded}
aria-label="show more"
>
<ExpandMoreIcon />
</ExpandMoreAdmin>
</CardActions>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
{props.order.products.map(product => (
<OrderAdminDetails orderId={props.order.id} productOrdered={product}></OrderAdminDetails>
))}
</CardContent>
</Collapse>

<Typography variant="body2" color="text.secondary">
Total: {props.order.subTotal}
</Typography>
<Typography variant="body2" color="text.secondary">
Shipping cost: {props.order.deliveryPrice}
</Typography>
</CardContent>
</Card>
);
}
46 changes: 46 additions & 0 deletions webapp/src/components/administrator/OrderAdminDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@


import Card from '@mui/material/Card';
import CardHeader from '@mui/material/CardHeader';
import CardMedia from '@mui/material/CardMedia';

import { baseApiEndPoint } from '../../api/api';

import { ProductOrdered } from "../../shared/shareddtypes";

import "../../css/OrderDetails.css";
import { Grid} from '@mui/material';

type ProductCardAdminProps = {
productOrdered: ProductOrdered;
orderId: string;
}
export default function OrderAdminDetails(props: ProductCardAdminProps) {

console.log(props);
const imgAdminPath = baseApiEndPoint + "/cars/" + props.productOrdered.product.image + "/" + props.productOrdered.product.image + " (1).jpg"
return (
<Card id="mainAdminCard">
<Grid container spacing={2}>
<Grid item xs={2.5}>
<CardHeader
title={props.productOrdered.product.name}
subheader={"Price: " + props.productOrdered.product.price + "€/unit Quantity: " + props.productOrdered.quantity}

/>
</Grid>

<Grid item xs={8}>
<CardMedia
id="cardImg"
component="img"
height="194"
image={imgAdminPath}
alt={props.productOrdered.product.name}
/>
</Grid>
</Grid>

</Card>
);
}
55 changes: 39 additions & 16 deletions webapp/src/components/fragments/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,30 @@ import Container from '@mui/material/Container';
import { ItemCart } from '../../shared/shareddtypes';

import '../../css/NavBar.css';
import { useNavigate, useSearchParams } from 'react-router-dom';


type NavBarProps = {
cart: ItemCart[];
};



export default function PrimarySearchAppBar(props: NavBarProps) {
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();


const [search, setSearch] = useState("");

let q : string = "";
function handleSearch() {
window.location.href = "/?q=" + q;
}

const SearchBar = () => (
<Container id="searchBarConatiner">
<form id="searchBarForm" action="/" method="get">
<form onSubmit={handleSearch}>
<TextField
variant="outlined"
type="text"
Expand All @@ -27,29 +42,34 @@ const SearchBar = () => (
name="q"
style={{ width: "100%" }}
sx={{ input: { color: 'white' } }}
/>
onChange={(e) => q=e.target.value}

/>

<Button
id="submitButton"
type="submit"
<Button
id="submitButton"
type="submit"
variant="contained"
>
>
<SearchIcon />
</Button>
</form>
</Container>
);

export default function PrimarySearchAppBar(props: NavBarProps) {


const [cartItemNumber, setCartItemNumber] = useState<number>(0);

useEffect(() => {

setCartItemNumber(props.cart.length);

}, []);

return (
<AppBar position="static">


<AppBar position="fixed">
<Toolbar id="navToolbar">

<a id="logoLink" href="/" >
Expand All @@ -60,12 +80,12 @@ export default function PrimarySearchAppBar(props: NavBarProps) {

<IconButton
style={{ marginRight: "1%" }}
size="large"
aria-label="shopping cart"
color="inherit"
size="large"
aria-label="shopping cart"
color="inherit"
href="/cart">
<Badge
badgeContent={props.cart.reduce((acc, i) => acc + i.quantity, 0)}
<Badge
badgeContent={props.cart.reduce((acc, i) => acc + i.quantity, 0)}
color="secondary"
>
<ShoppingCart />
Expand All @@ -81,10 +101,13 @@ export default function PrimarySearchAppBar(props: NavBarProps) {
href="/login"
style={{ marginRight: "5%" }}
>
<AccountCircle/>
<AccountCircle />
</IconButton>

</Toolbar>
</AppBar>



);
}
}
Loading

0 comments on commit e34b131

Please sign in to comment.