Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
96b23a0
initialized cms frontend merch api
klau1011 Oct 17, 2023
9871af4
Add product action to state
klau1011 Oct 20, 2023
57d782f
Added state reducer and selector for products
klau1011 Oct 20, 2023
9c78245
Add products hook and fixed api typo
klau1011 Oct 20, 2023
be110c0
Fixed product state functions
klau1011 Oct 20, 2023
6b811c5
add product info reducer
klau1011 Oct 20, 2023
5b46745
Added products-form hook
klau1011 Oct 21, 2023
7899b9f
Init merch page
klau1011 Oct 27, 2023
cd33af6
Added Product edit btn
klau1011 Oct 28, 2023
9c7eb17
added products and variations functionality
klau1011 Dec 31, 2023
e7e94f8
added products/variations to landing page
klau1011 Jan 2, 2024
28778c2
functionality for variations
klau1011 Jan 13, 2024
38a265b
initialized cms frontend merch api
klau1011 Oct 17, 2023
0029ee7
Add product action to state
klau1011 Oct 20, 2023
d41f1b4
Added state reducer and selector for products
klau1011 Oct 20, 2023
a651f97
Add products hook and fixed api typo
klau1011 Oct 20, 2023
0e158bb
Fixed product state functions
klau1011 Oct 20, 2023
227f53e
add product info reducer
klau1011 Oct 20, 2023
63910fb
Added products-form hook
klau1011 Oct 21, 2023
91022b2
Init merch page
klau1011 Oct 27, 2023
6161247
Added Product edit btn
klau1011 Oct 28, 2023
8edd295
added products and variations functionality
klau1011 Dec 31, 2023
8dfe074
added products/variations to landing page
klau1011 Jan 2, 2024
aaf2d43
functionality for variations
klau1011 Jan 13, 2024
13a4a69
Merge branch 'merch-edit-form' of https://github.com/waterloop/waterl…
klau1011 Jan 18, 2024
f4b79ff
optimizations
klau1011 Jan 25, 2024
7403e52
streamlined products and variations in a flow
klau1011 Feb 4, 2024
dbb4373
remove unnecessary imports
klau1011 Feb 6, 2024
e9c11bd
cleanup products and variations
klau1011 Feb 8, 2024
6531060
cleanup logs and merged edit/add variation
klau1011 Feb 13, 2024
8a2eaec
refactor naming to merch store
klau1011 Feb 14, 2024
976bef7
updated photo path
klau1011 Feb 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/frontend/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import PostingsRouter from './pages/postings/Postings.router';
import SponsorsRouter from './pages/sponsors/Sponsors.router';
import GeeseRouter from './pages/geese/Geese.router';
import TeamDescriptionsRouter from './pages/team-descriptions/TeamDescriptions.router';
import MerchStoreRouter from './pages/merch-store/Products.router'

import { addAuthTokenToRequests } from './api/server';
import BlogsRouter from './pages/blogs/Blogs.Router';

Expand Down Expand Up @@ -69,6 +71,11 @@ const App = () => {
<TopBar />
<BlogsRouter />
</Route>
<Route path="/products">
{!token && <Redirect to="/sign-in" />}
<TopBar />
<MerchStoreRouter />
</Route>
<Route component={NotFoundPage}>
{/* {!token && <Redirect to="/sign-in" />} */}
<TopBar />
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import openingsDescription from './openings-description';
import sponsors from './sponsors';
import geeseInfo from './geese-info';
import blogs from './blogs';
import merchStore from './merch-store'

export default {
google: google(server),
Expand All @@ -16,5 +17,6 @@ export default {
formUpload: formUpload(server),
openingsDescription: openingsDescription(server),
geeseInfo: geeseInfo(server),
blogs: blogs(server)
blogs: blogs(server),
merchStore: merchStore(server)
};
26 changes: 26 additions & 0 deletions src/frontend/api/merch-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const getProducts = (server) => () => server.get('/api/products');
const getProductVariations = (server) => (id) =>
server.get(`/api/products/${id}/variations`);
const updateProduct = (server) => (id, updatedProductInfo) =>
server.patch(`/api/products/${id}`, updatedProductInfo);
const updateProductVariation = (server) => (variationId, productId, updatedVariationInfo) =>
server.patch(`/api/products/${productId}/variations/${variationId}`, updatedVariationInfo);
const addProduct = (server) => (product) =>
server.post(`/api/products`, product);
const addProductVariation = (server) => (productVariation) =>
server.post(`/api/products/${productVariation.productId}/variations`, productVariation);
const deleteProduct = (server) => (id) =>
server.delete(`/api/products/${id}`);
const deleteProductVariation = (server) => (productId, variationId) =>
server.delete(`/api/products/${productId}/variations/${variationId}`);

export default (server) => ({
getProducts: getProducts(server),
getProductVariations: getProductVariations(server),
updateProduct: updateProduct(server),
updateProductVariation:updateProductVariation(server),
addProduct: addProduct(server),
addProductVariation: addProductVariation(server),
deleteProduct: deleteProduct(server),
deleteProductVariation: deleteProductVariation(server),
});
29 changes: 29 additions & 0 deletions src/frontend/assets/page-icons/merch.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/frontend/hooks/goose-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import moment from 'moment';

const useGooseForm = () => {
const [gooseName, setGooseName] = useState('');
const [description, setDescription] = useState('');
const [description, setDescription] = useState('');
const [images, setImages] = useState([]);
const [imageUrls, setImageUrls] = useState([]);
const [imagesToDelete, setImagesToDelete] = useState([]);
Expand Down
199 changes: 199 additions & 0 deletions src/frontend/hooks/product-variations-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { useState, useEffect, useCallback } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import useProductVariations from './product-variations';
import useProducts from './products';
import api from '../api';
import moment from 'moment';

const useProductVariationsForm = () => {
const [productName, setProductName] = useState('');
const [productId, setProductId] = useState(null);
const [variationName, setVariationName] = useState('');
const [price, setPrice] = useState(null);
const [stock, setStock] = useState(null);
const [picture, setPicture] = useState(null);
const [pictureUrl, setPictureURL] = useState(null);
const [showModal, setShowModal] = useState(false);
const history = useHistory();
const params = useParams();
const { products } = useProducts();
const { productVariations } = useProductVariations();

useEffect(() => {
(async () => {
try {
let variation = {};
let product = {};

if (params.productId) {
product = await products.find(
(product) => product.id == params.productId,
);
}

if (params.variationId) {
const productVariationId = parseInt(params.variationId, 10);
variation = productVariations.find(
(variation) => variation.id === productVariationId,
);
}
setProductName(product.name);
setProductId(product.id);

setVariationName(variation.variationName);
setPrice(variation.price);
setStock(variation.stock);
setPictureURL(variation.picture);
} catch (err) {
console.error("Error init'ing product variation form info", err);
}
})();
}, [productVariations, products, params.variationId, params.productId]);

const imageUpload = (image) => {
setPicture(image);
setPictureURL(URL.createObjectURL(image));
};

const imageDelete = useCallback(() => {
setPicture(null);
setPictureURL(null);
}, [setPicture]);

const openModal = () => {
setShowModal(true);
};

const closeModal = () => {
setShowModal(false);
};

const closeForm = useCallback(() => {
history.push(`/products/${productId}/variations`);
}, [history, productId]);

const saveForm = useCallback(async () => {
try {
let newPictureUrl = pictureUrl;

if (picture instanceof File) {
const data = new FormData();
data.append('files', picture, picture.name);
const res = await api.formUpload(data);

newPictureUrl = res.data.data[0];
}

const currentDateTime = new Date();
const unixTimestamp = currentDateTime.getTime();

const productVariationInfo = {
variationName,
productId,
price,
stock,
picture: newPictureUrl,
lastUpdated: unixTimestamp,
};

if (variationName && price && stock && pictureUrl) {
if (params.variationId) {
await api.merchStore.updateProductVariation(
params.variationId,
productId,
productVariationInfo,
);
} else {
await api.merchStore.addProductVariation({
...productVariationInfo,
productId,
});
}
setPicture(picture);
} else {
throw new Error('Please fill all the required fields.');
}
// onSuccess:
closeForm();
} catch (e) {
// TODO: Display "could not add/update" error to the user as dialogue.
console.error(e);
}
}, [
params,
variationName,
price,
stock,
closeForm,
picture,
productId,
pictureUrl,
]);

const deleteForm = useCallback(async () => {
try {
if (params.variationId) {
const productVariationId = parseInt(params.variationId, 10);
const variation = productVariations.find(
(variation) => variation.id === productVariationId,
);

if (variation) {
const product = products.find(
(product) => product.id === variation.productId,
);

if (product) {
const productId = product.id;
await api.merchStore.deleteProductVariation(
productId,
params.variationId,
);
}
}
}
closeForm();
} catch (error) {
console.error('Error deleting product variation:', error);
closeForm();
}
}, [params, productVariations, products, closeForm]);

const getLastUpdated = useCallback(() => {
const variation = productVariations.find(
(variation) => variation.id === parseInt(params.variationId, 10),
);
if (variation) {
return moment.utc(variation.updatedAt).local().format('MMMM D, YYYY');
}
return '';
}, [productVariations, params.variationId]);

return {
productName,
setProductName,
productId,
setProductId,
variationName,
setVariationName,
price,
setPrice,
picture,
setPicture,
pictureUrl,
setPictureURL,
stock,
setStock,
imageUpload,
imageDelete,
closeForm,
saveForm,
deleteForm,
getLastUpdated,
showModal,
openModal,
closeModal,
};
};

export default useProductVariationsForm;
55 changes: 55 additions & 0 deletions src/frontend/hooks/product-variations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import api from '../api';
import { useDispatch, useSelector } from 'react-redux';
import * as productVariationActions from '../state/product-variations/actions';
import * as productVariationSelectors from '../state/product-variations/selectors';

const useProductVariations = () => {
const params = useParams();

const dispatch = useDispatch();
const productVariations = useSelector(
productVariationSelectors.allVariations,
);
useEffect(() => {
(async () => {
try {
let newProductVariations = [];
if (params.productId) {
const productVariationsResponse =
await api.merchStore.getProductVariations(params.productId);
// get all products
const productsResponse = await api.merchStore.getProducts();
const products = productsResponse.data;

const productName = products.find(
(product) => product.id == params.productId,
).name;

for (const productVariation of productVariationsResponse.data) {
const variation = { ...productVariation, productName };
newProductVariations = [...newProductVariations, variation];
}
}

dispatch(
productVariationActions.updateProductVariationsInfo(
newProductVariations,
),
);
} catch (err) {
console.error(
'error fetching products in product-variations.js: ',
err,
);
}
})();
}, [dispatch, params.productId, params.variationId]);

return {
productVariations,
};
};

export default useProductVariations;
Loading