Skip to content

Commit

Permalink
finished add item
Browse files Browse the repository at this point in the history
  • Loading branch information
zmattes04 committed May 26, 2024
1 parent ba4939b commit c24150b
Show file tree
Hide file tree
Showing 2 changed files with 370 additions and 1 deletion.
4 changes: 3 additions & 1 deletion frontend/src/components/Basket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { useEffect, useState } from "react";
import BasketItem from "./BasketItem";
import "../styles/Basket.css";
import NewItemOptions from "./NewItemOptions";

export interface Basket {
basketName: string;
Expand Down Expand Up @@ -166,7 +167,8 @@ const BasketComp = ({ basketId, stateObj, isOwnerView }: Props) => {
>
<Flex justifyContent="space-between">
<Heading>Basket Items</Heading>
<Button>ADD ITEM</Button>

<NewItemOptions basket={basketId} updateBasket={setBasket} />
</Flex>
<Divider borderColor="black" marginTop="1%" />
<VStack>
Expand Down
367 changes: 367 additions & 0 deletions frontend/src/components/NewItemOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
import {
Button,
Flex,
Box,
Popover,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
useDisclosure,
FormControl,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
} from "@chakra-ui/react";
import { FormEvent, useState } from "react";
import "../styles/JoinGroup.css";

const NewItemOptions = ({
basket,
updateBasket,
}: {
basket: any;
updateBasket: any;
}) => {
const createItem = async (
name: string,
toShare: boolean,
isPrivate: boolean,
type: string,
notes: string,
price: number,
quantity: number,
) => {
const res = await fetch(`http://localhost:3001/baskets/${basket}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});

if (res.ok) {
const currentBasket = await res.json();
const payload = {
name,
toShare,
isPrivate,
type,
notes,
price,
quantity,
basket: [currentBasket._id],
};

const promise = await fetch("http://localhost:3001/items/", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
body: JSON.stringify(payload),
});

if (promise.status === 201) {
const data = await promise.json();
const newData = [...currentBasket.items, data._id];
const basketPromise = await fetch(
`http://localhost:3001/baskets/${currentBasket._id}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
body: JSON.stringify({ items: newData }),
},
);

if (basketPromise.status === 200) {
const basketData = await basketPromise.json();
updateBasket(basketData);
}
}
}
};

return (
<Flex
justifyContent="space-between"
alignItems="bottom"
marginTop="10px"
width="15%"
>
<CreateItem postItem={createItem} />
</Flex>
);
};

interface CreateProps {
postItem: (
name: string,
toShare: boolean,
isPrivate: boolean,
type: string,
notes: string,
price: number,
quantity: number,
) => void;
}

const CreateItem = ({ postItem }: CreateProps) => {
const [item, setItem] = useState({
name: "",
toShare: false,
isPrivate: false,
type: "",
notes: "",
price: 0,
quantity: 0,
});
const [errored, setError] = useState({ state: false, msg: "" });
const { onOpen, onClose, isOpen } = useDisclosure();

const handleChange = (event: FormEvent<HTMLInputElement>) => {
const { name, value, type, checked } = event.currentTarget;

setItem((prevItem) => ({
...prevItem,
[name]: type === "checkbox" ? checked : value,
}));
};

const handleSubmit = () => {
console.log(item);
postItem(
item.name,
item.toShare,
item.isPrivate,
item.type,
item.notes === "" ? "No description given" : item.notes,
item.price,
item.quantity,
);
setItem({
name: "",
toShare: false,
isPrivate: false,
type: "",
notes: "",
price: 0,
quantity: 0,
});
};

const handleNumberInputChangePrice = (valueAsString: string) => {
// Convert string to float because the input will provide a formatted string with a dollar sign
const valueAsNumber = parseFloat(valueAsString.replace(/^\$/, ""));
setItem((prevItem) => ({ ...prevItem, price: valueAsNumber }));
};

const handleNumberInputChangeQuantity = (
valueAsString: string,
valueAsNumber: number,
) => {
console.log(valueAsString);
setItem((prevItem) => ({ ...prevItem, quantity: valueAsNumber }));
};

return (
<>
<Popover
isOpen={isOpen}
onOpen={onOpen}
onClose={() => {
setError({ state: false, msg: "" });
onClose();
}}
placement="bottom-end"
autoFocus
closeOnBlur
>
<PopoverTrigger>
<Button
color="var(--col-dark)"
borderRadius="30px"
borderColor="var(--col-secondary)"
borderWidth="3px"
width="100px"
height="30px"
marginRight="2px"
fontWeight="300"
fontSize="14px"
letterSpacing="1px"
>
ADD ITEM
</Button>
</PopoverTrigger>
<PopoverContent
bgColor="var(--col-bright)"
borderWidth="5px"
borderColor="var(--col-secondary)"
p="20px"
borderRadius="10px"
>
<PopoverHeader
display="flex"
justifyContent="center"
fontSize="1.4rem"
color="var(--col-secondary)"
borderBottomColor="var(--col-dark)"
padding="0px"
paddingBottom="5px"
marginBottom="10px"
>
Create new Item
</PopoverHeader>
<form>
<label htmlFor="name" className="i-b">
Name
</label>
<input
type="text"
name="name"
id="name"
value={item.name}
className="i-b text-container"
onChange={handleChange}
/>
<label htmlFor="desc" className="i-b">
Description
</label>
<input
type="text"
name="notes"
id="notes"
value={item.notes}
onChange={handleChange}
className="i-b text-container multiline"
/>
<label htmlFor="desc" className="i-b">
Type
</label>
<input
type="text"
name="type"
id="type"
value={item.type}
onChange={handleChange}
className="i-b text-container multiline"
/>
<PopoverFooter
display="flex"
flexDirection="column"
justifyContent="space-between"
alignItems="center"
>
<Flex width="100%" justifyContent="space-between" mb="10px">
<FormControl width="45%">
<label htmlFor="desc" className="i-b">
Price
</label>
<NumberInput
onChange={(valueAsString) =>
handleNumberInputChangePrice(valueAsString)
}
value={item.price || ""}
precision={2} // Allows two decimal places
step={1.0} // Step size for increment and decrement steppers
max={100000}
min={0}
clampValueOnBlur={false} // Prevents automatic rounding on blur
format={(val) => `$${val}`} // Prefixing the dollar sign
>
<NumberInputField
id="price"
borderColor="var(--col-dark)"
_hover={{ bg: "var(--col-tertiary)" }}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</FormControl>
<FormControl width="45%">
<label htmlFor="desc" className="i-b">
Quantity
</label>
<NumberInput
onChange={handleNumberInputChangeQuantity}
value={item.quantity || ""}
max={100000}
min={0}
>
<NumberInputField
borderColor="var(--col-dark)"
_hover={{ bg: "var(--col-tertiary)" }}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</FormControl>
</Flex>
<Box
display="flex"
width="100%"
justifyContent="space-between"
mb="10px"
>
<Box>
<input
type="checkbox"
name="toShare"
id="toShare"
checked={item.toShare === true}
onChange={handleChange}
className="checkbox"
/>
<label htmlFor="toShare" className="sidenote">
Sharable
</label>
</Box>
<Box>
<input
type="checkbox"
name="isPrivate"
id="isPrivate"
checked={item.isPrivate === true}
onChange={handleChange}
className="checkbox"
/>
<label htmlFor="isPrivate" className="sidenote">
Private
</label>
</Box>
</Box>
<Box
display={`${errored.state ? "inherit" : "none"}`}
color="red"
fontSize="0.6rem"
wordBreak="break-word"
width="100%"
mb="10px"
>
Error: {errored.msg}
</Box>
<input
type="button"
value="Add Item"
onClick={handleSubmit}
className="submit-button"
/>
</PopoverFooter>
</form>
<PopoverCloseButton color="var(--col-dark)" />
</PopoverContent>
</Popover>
</>
);
};
export default NewItemOptions;

0 comments on commit c24150b

Please sign in to comment.