+ <>
+
+ >
);
};
diff --git a/src/frontend/pages/features/components/FeaturePreview.js b/src/frontend/pages/features/components/FeaturePreview.js
index 7b311cf..d421681 100644
--- a/src/frontend/pages/features/components/FeaturePreview.js
+++ b/src/frontend/pages/features/components/FeaturePreview.js
@@ -81,22 +81,23 @@ const ButtonContainer = styled.div`
`;
const FeaturePreview = ({
- title,
- body,
- img,
+ name,
+ description,
+ picture,
onEdit,
onDelete,
-}) => (
+}) => {
+ return (
- {title}
- {body}
+ {name}
+ {description}
@@ -104,6 +105,6 @@ const FeaturePreview = ({
Delete
- );
+ )};
export default FeaturePreview;
diff --git a/src/frontend/pages/features/hooks/features.js b/src/frontend/pages/features/hooks/features.js
deleted file mode 100644
index 1f901b0..0000000
--- a/src/frontend/pages/features/hooks/features.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { useCallback } from 'react';
-
-// This would come from the server (backend) eventually
-const mockFeatures = [
- {
- id: 0,
- title: 'Feat1 - Medium Body text',
- body: 'To scale to Hyperloop speeds, we need contactless propulsion. LIMs provide a way to levitate, stabilize, and propel, all in the same package, and they require a simple metal track with no magnets - perfect for minimizing infrastructure cost.',
- img: 'https://teamwaterloop.ca/static/media/LIM.46c5a4f0.png'
- },
- {
- id: 1,
- title: 'Feat1 - Short Body text',
- body: 'To scale to Hyperloop speeds, we need contactless propulsion.',
- img: 'https://teamwaterloop.ca/static/media/LIM.46c5a4f0.png'
- },
- {
- id: 2,
- title: 'Feat1 - Long Body Text',
- body: 'To scale to Hyperloop speeds, we need contactless propulsion. LIMs provide a way to levitate, stabilize, and propel, all in the same package, and they require a simple metal track with no magnets - perfect for minimizing infrastructure cost.To scale to Hyperloop speeds, we need contactless propulsion. LIMs provide a way to levitate, stabilize, and propel, all in the same package, and they require a simple metal track with no magnets - perfect for minimizing infrastructure cost.To scale to Hyperloop speeds, we need contactless propulsion. LIMs provide a way to levitate, stabilize, and propel, all in the same package, and they require a simple metal track with no magnets - perfect for minimizing infrastructure cost.To scale to Hyperloop speeds, we need contactless propulsion. LIMs provide a way to levitate, stabilize, and propel, all in the same package, and they require a simple metal track with no magnets - perfect for minimizing infrastructure cost.',
- img: 'https://teamwaterloop.ca/static/media/LIM.46c5a4f0.png'
- },
-];
-
-const useFeatures = () => {
- /**
- * TODO:
- * These all need to make calls to the server eventually
- */
- const create = useCallback(
- (feature) => {
- console.log(`Creating feature The new Feature is ${feature}`);
- }, []);
-
- const edit = useCallback(
- (id, updatedFeature) => {
- console.log(`Updating feature with id: ${id}. The new Feature is ${updatedFeature}`);
- }, []);
-
- const del = useCallback(
- (id) => {
- console.log(`Deleting feature with id: ${id}.`);
- }, []);
-
- return {
- features: mockFeatures,
- createFeature: create,
- editFeature: edit,
- deleteFeature: del,
- };
-}
-
-export default useFeatures;
diff --git a/src/frontend/pages/features/hooks/geese-features-form.js b/src/frontend/pages/features/hooks/geese-features-form.js
new file mode 100644
index 0000000..0114a38
--- /dev/null
+++ b/src/frontend/pages/features/hooks/geese-features-form.js
@@ -0,0 +1,131 @@
+import { useState, useEffect, useCallback } from 'react';
+import { useHistory, useParams } from 'react-router-dom';
+import useGeeseFeatures from './geese-features';
+import api from '../../../api';
+
+const useGeeseFeaturesForm = () => {
+ const [featureName, setFeatureName] = useState('');
+ const [picture, setPicture] = useState('');
+ const [pictureUrl, setPictureUrl] = useState('');
+ const [description, setDescription] = useState('');
+ const [showModal, setShowModal] = useState(false);
+
+ const history = useHistory();
+ const params = useParams();
+
+ const { geeseFeatures } = useGeeseFeatures();
+
+ useEffect(() => {
+ if (geeseFeatures?.length > 0) {
+ (async () => {
+ try {
+ if (params.featureId) {
+ const featureId = parseInt(params.featureId, 10);
+
+ const feature = geeseFeatures.find(
+ (feature) => feature.id === featureId,
+ );
+
+ if (feature) {
+ setFeatureName(feature.name);
+ setPictureUrl(feature.picture);
+ setDescription(feature.description);
+ }
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ })();
+ }
+ }, [
+ setFeatureName,
+ setPicture,
+ setPictureUrl,
+ setDescription,
+ params,
+ geeseFeatures,
+ ]);
+
+ const imageUpload = (image) => {
+ setPicture(image);
+ setPictureUrl(URL.createObjectURL(image));
+ };
+
+ const imageDelete = useCallback(() => {
+ setPicture('');
+ setPictureUrl('')
+ }, [setPicture]);
+
+ const openModal = () => {
+ setShowModal(true);
+ };
+
+ const closeModal = () => {
+ setShowModal(false);
+ };
+
+ const closeForm = useCallback(() => {
+ history.push('/features');
+ }, [history]);
+
+ const saveForm = useCallback(async () => {
+ try {
+ let newPictureUrl = pictureUrl;
+
+ if (picture instanceof File) {
+ const formData = new FormData();
+ formData.append('files', picture, picture.name);
+ const response = await api.formUpload(formData);
+
+ if (!response.data.data) {
+ throw new Error('Error uploading image');
+ }
+
+ newPictureUrl = response.data.data[0];
+ }
+
+
+ const feature = {
+ name: featureName,
+ picture: newPictureUrl,
+ description,
+ };
+
+ if (featureName && newPictureUrl && description) {
+ if (!params.featureId) {
+ await api.geeseFeatures.addGeeseFeature(feature);
+ } else {
+ await api.geeseFeatures.updateGeeseFeature(params.featureId, feature);
+ }
+ closeForm()
+
+ } else {
+ throw new Error('Fields must be non empty');
+ }
+ } catch (error) {
+ console.log(error)
+ } finally{
+
+ }
+ }, [featureName, picture, pictureUrl, description, params]);
+
+ return {
+ featureName,
+ setFeatureName,
+ picture,
+ setPicture,
+ pictureUrl,
+ setPictureUrl,
+ description,
+ setDescription,
+ imageUpload,
+ imageDelete,
+ saveForm,
+ closeForm,
+ showModal,
+ openModal,
+ closeModal,
+ };
+};
+
+export default useGeeseFeaturesForm;
diff --git a/src/frontend/pages/features/hooks/geese-features.js b/src/frontend/pages/features/hooks/geese-features.js
new file mode 100644
index 0000000..cfcd2f2
--- /dev/null
+++ b/src/frontend/pages/features/hooks/geese-features.js
@@ -0,0 +1,41 @@
+import { useCallback, useEffect } from 'react';
+import api from '../../../api';
+import { useDispatch, useSelector } from 'react-redux';
+import * as geeseFeaturesActions from '../../../state/geese-features/actions'
+import * as geeseFeaturesSelectors from '../../../state/geese-features/selectors';
+
+const useGeeseFeatures = () => {
+ const dispatch = useDispatch();
+ const geeseFeatures = useSelector(
+ geeseFeaturesSelectors.allGeeseFeatures,
+ ).features;
+
+ useEffect(() => {
+ (async () => {
+ try {
+ const geeseFeaturesResponse =
+ await api.geeseFeatures.getGeeseFeatures();
+
+ const newGeeseFeatures = geeseFeaturesResponse.data;
+
+ dispatch(geeseFeaturesActions.updateGeeseFeatures(newGeeseFeatures));
+ } catch (error) {
+ console.log(error);
+ }
+ })();
+ }, [dispatch]);
+
+ const deleteFeature = useCallback(async (featureId) => {
+ await api.geeseFeatures.deleteGeeseFeature(featureId);
+ const geeseFeaturesResponse = await api.geeseFeatures.getGeeseFeatures();
+ const newGeeseFeatures = geeseFeaturesResponse.data;
+ dispatch(geeseFeaturesActions.updateGeeseFeatures(newGeeseFeatures));
+ }, []);
+
+ return {
+ geeseFeatures,
+ deleteFeature,
+ };
+};
+
+export default useGeeseFeatures;
diff --git a/src/frontend/pages/sponsors/forms/EditSponsors.js b/src/frontend/pages/sponsors/forms/EditSponsors.js
index 1df6197..a9bcc3f 100644
--- a/src/frontend/pages/sponsors/forms/EditSponsors.js
+++ b/src/frontend/pages/sponsors/forms/EditSponsors.js
@@ -9,6 +9,7 @@ import UnstyledFormContainer from '../../../components/FormContainer';
import UnstyledTextInput from '../../../components/TextInput';
import UnstyledSelector from '../../../components/Selector';
import Button from '../../../components/Button';
+
import UnstyledImagePreview from '../../../components/ImagePreview';
import Dialog from '../../../components/Dialog';
diff --git a/src/frontend/state/action-types.js b/src/frontend/state/action-types.js
index aa242d3..ec513e1 100644
--- a/src/frontend/state/action-types.js
+++ b/src/frontend/state/action-types.js
@@ -14,4 +14,6 @@ export const SPONSORS_SET_SPONSOR_TIERS = 'SPONSORS_SET_SPONSOR_TIERS';
export const GEESE_INFO_SET_GEESE_INFO = 'GEESE_INFO_SET_GEESE_INFO';
-export const UPDATE_BLOG_INFO = 'UPDATE_BLOG_INFO';
\ No newline at end of file
+export const UPDATE_BLOG_INFO = 'UPDATE_BLOG_INFO';
+
+export const GEESE_FEATURES_SET_GEESE_FEATURE = 'GEESE_FEATURES_SET_GEESE_FEATURE'
\ No newline at end of file
diff --git a/src/frontend/state/geese-features/actions.js b/src/frontend/state/geese-features/actions.js
new file mode 100644
index 0000000..cc6f529
--- /dev/null
+++ b/src/frontend/state/geese-features/actions.js
@@ -0,0 +1,6 @@
+import * as actionTypes from '../action-types'
+
+export const updateGeeseFeatures = (geeseFeatures) => ({
+ type: actionTypes.GEESE_FEATURES_SET_GEESE_FEATURE,
+ payload: geeseFeatures
+})
\ No newline at end of file
diff --git a/src/frontend/state/geese-features/reducer.js b/src/frontend/state/geese-features/reducer.js
new file mode 100644
index 0000000..c578b6c
--- /dev/null
+++ b/src/frontend/state/geese-features/reducer.js
@@ -0,0 +1,19 @@
+import * as actionTypes from '../action-types';
+
+const initialState = {
+ allGeeseFeatures: [],
+};
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case actionTypes.GEESE_FEATURES_SET_GEESE_FEATURE:
+ return {
+ ...state,
+ allGeeseFeatures: action.payload,
+ };
+ default:
+ return state;
+ }
+};
+
+export default reducer;
diff --git a/src/frontend/state/geese-features/selectors.js b/src/frontend/state/geese-features/selectors.js
new file mode 100644
index 0000000..6c3f019
--- /dev/null
+++ b/src/frontend/state/geese-features/selectors.js
@@ -0,0 +1 @@
+export const allGeeseFeatures = (state) => state.geeseFeatures.allGeeseFeatures
\ No newline at end of file
diff --git a/src/frontend/state/reducer.js b/src/frontend/state/reducer.js
index e3e9d81..d91aa49 100644
--- a/src/frontend/state/reducer.js
+++ b/src/frontend/state/reducer.js
@@ -5,6 +5,7 @@ import teamsReducer from './teams/reducer';
import sponsorsReducer from './sponsors/reducer';
import geeseInfoReducer from './geese-info/reducer';
import blogInfoReducer from './blogs/reducer';
+import geeseFeaturesReducer from './geese-features/reducer'
export default combineReducers({
user: userReducer,
@@ -12,5 +13,6 @@ export default combineReducers({
teams: teamsReducer,
sponsors: sponsorsReducer,
geeseInfo: geeseInfoReducer,
- blogInfo: blogInfoReducer
+ blogInfo: blogInfoReducer,
+ geeseFeatures: geeseFeaturesReducer
});