Skip to content

First Pass at E-Commerce Front End Service#1

Open
apradoada wants to merge 28 commits into
mainfrom
AP-FE-Updates
Open

First Pass at E-Commerce Front End Service#1
apradoada wants to merge 28 commits into
mainfrom
AP-FE-Updates

Conversation

@apradoada
Copy link
Copy Markdown
Contributor

This is a basic front end for the E-Commerce Site. Minimal testing and styling currently that will be built out later.

Current abilities:

Login - User can login or create a new account, which will be sent to the User Microservice
Products - If the user is an admin, they can see all the products, delete them or update stock on the Home page. If the user is not an admin, they can see all products and add them to a cart.
Orders - As a cart is created, users can submit a cart to the Order microservice. Admins can see all orders. Non-Admins can see their orders.
User - Users can update their own information.

Copy link
Copy Markdown

@mikellewade mikellewade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look through the application and great as a first pass! Left some comments mainly about making it more DRY so students have less to read through, conceptual questions and then some niceties. Let me know if you have any questions.

Comment thread src/components/UserForm.tsx Outdated
Comment on lines +46 to +58
<div>
<label>ID</label>
<input type="text" value={user.id} disabled />
</div>
<div>
<label>First Name</label>
<input
type="text"
name="firstName"
value={formData.firstName}
onChange={handleChange}
/>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could make a helper function that constructs the controlled inputs. If you look in Flasky Frontend, you will see the pattern. I believe it's in one of the Forms sessions. That way the code will be more DRY. Would most likely need to alter the function, of course.

Comment thread src/components/UserForm.tsx Outdated
}));
};

const handleSubmit = (e: FormEvent) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In VScode it says that FormEvent is deprecated. It states that the following:

" FormEvent doesn't actually exist. You probably meant to use ChangeEvent, InputEvent, SubmitEvent, or just SyntheticEvent instead depending on the event type."

Based on the function, I am assuming that you will need a SubmitEvent instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this one when I was refactoring! Changed it to a SubmitEvent!

Comment thread src/components/UserForm.tsx Outdated

const [status, setStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the React.ChangeEvent<HTMLInputElement>, I think you could remove the React prefix and just import it at the top of the file like the FormEvent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

Comment thread src/components/CreateUserForm.tsx Outdated
Comment on lines +17 to +25
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
};

const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like in the UserForm.tsx, just a suggestion to import the different events that the top of the file to remove the React prefix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

Comment thread src/components/CreateUserForm.tsx Outdated
Comment on lines +43 to +61
<form onSubmit={handleSubmit}>
<div>
<label>First Name</label>
<input
type="text"
name="firstName"
value={formData.firstName}
onChange={handleChange}
/>
</div>
<div>
<label>Last Name</label>
<input
type="text"
name="lastName"
value={formData.lastName}
onChange={handleChange}
/>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in UserForm.tsx, you could DRY this up by making the helper function that constructs controlled inputs.

Comment thread src/components/OrderList.tsx Outdated

const orderCards = orders.map((order: Order) => {
return (
<li key={order.id}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, you have an li item tag here and also inside the OrderCard.tsx component. React is throwing a warning about having li tags nested within li tags. I think it is safe to remove the one inside the OrderCard.tsx file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to a dummy parent tag!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Comment thread src/components/ProductCard.tsx Outdated
<li><button onClick={()=>deleteProduct(id)}>Delete Item</button></li>
</>
) : (
<li><button onClick={() => addItem({ productId: id, name, price, quantity: numItem })}>Add to Cart</button></li>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe add some user feedback here for when the item is added? At first I didn't think that it worked .

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some very simple feedback!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this file, there are a few times you reference an individual item as i, could we choose a variable name that is more descriptive of the data that it is referencing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

Comment thread src/context/CartContext.tsx Outdated
Comment on lines +61 to +62
userId: user?.id ?? '',
userEmail: user?.email ?? '',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually speaking, I would assume that any consumer that needs this information would call the useAuth hook to retrieve this information. Would we want to remove this from the Cart context?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@apradoada
Copy link
Copy Markdown
Contributor Author

Thank you so much for looking through this and for your suggestions! I think I got them all with a couple that I'll save for another update. The big ones I got were:

  • Controlled Inputs elements in CreateUserForm and ProductForm
  • Deleting the UserForm and allow the CreateUserForm to run different API calls depending on if a user is logged in.
  • Set up image functionality with S3 and Lambda
  • Small syntax changes

Copy link
Copy Markdown

@mikellewade mikellewade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So sorry, I did not see that you submitted all the changes. The reply in the thread was late at night so I didn't see the notification. Apologies! But looks great, just commented about a couple of things. Let me know what you think or if you have any questions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice! I didn't even think about this approach.

e.preventDefault();
setStatus('saving');

if(user === null){
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(user === null){
if (user === null){

Comment on lines +34 to +52
axios.post(`${userUrl}/users/`, {
first_name: formData.firstName,
last_name: formData.lastName,
email: formData.email,
is_admin: formData.isAdmin,
})
.then(() => setStatus('saved'))
.catch(() => setStatus('error'));
}
else {
axios.put(`${userUrl}/users/${user?.id}`, {
first_name: formData.firstName,
last_name: formData.lastName,
email: formData.email,
is_admin: formData.isAdmin,
})
.then(() => setStatus('saved'))
.catch(() => setStatus('error'));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the only difference between these calls are the HTTP methods (put vs post) and the URLs. To make it more DRY you could condense this to a function and then have a conditional. Something like this perhaps:

const saveUser = () => {
    const method = user ? 'put' : 'post';
    const url = user ? `${userUrl}/users/${user.id}` : `${userUrl}/users/`;

    axios[method](url, {
        first_name: formData.firstName,
        last_name: formData.lastName,
        email: formData.email,
        is_admin: formData.isAdmin,
    })
    .then(() => setStatus('saved'))
    .catch(() => setStatus('error'));
};


const orderCards = orders.map((order: Order) => {
return (
<li key={order.id}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

const inputName = event.target.name;

setFormState(prev => ({...prev,
[inputName]: inputName ==='price'|| inputName === 'stock'? Number(inputValue) : inputValue}));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[inputName]: inputName ==='price'|| inputName === 'stock'? Number(inputValue) : inputValue}));
[inputName]: inputName ==='price'|| inputName === 'stock' ? Number(inputValue) : inputValue}));

onChange={handleChange}
/>
);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could refactor the you make controlled components into a single one and pull it out into a utility file so the code would be more DRY. But I don't think that is the highest priority.


return (
<div>
<h1> Add User </h1>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A conditional based on if the user is null or not for Update User or Add User?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just some styling on this component so it is easier to digest the information being displayed?

Comment thread src/pages/Home.tsx
Comment on lines +13 to +37
const addStock = (product: Product, newStock: number) => {
return { ...product, stock: newStock};
};

const getAllProductsAPI = () => {
return axios.get(`${productUrl}/products/`)
.then(response => response.data)
};
const updateProductAPI = (id: string, updateData: Partial<Product>) => {
return axios.patch(`${productUrl}/products/${id}`, updateData)
}
const deleteProductAPI = (id : string) => {
return axios.delete(`${productUrl}/products/${id}`)
};
const convertFromAPI = (apiProduct: APIProduct): Product => {
return {
...apiProduct,
id: apiProduct.productId,
};

};
const addProductAPI = (newProduct: Omit<Product, 'id'>) => {
return axios.post(`${productUrl}/products/`, newProduct)
.then(response => response.data)
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine by me.

Comment on lines +17 to +21
id: String(fetchedUser.id),
firstName: fetchedUser.first_name,
lastName: fetchedUser.last_name,
email: fetchedUser.email,
isAdmin: fetchedUser.is_admin // Map snake_case to camelCase
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't think its terribly important.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants