瀏覽代碼

Updating categories using form

unknown 4 年之前
父節點
當前提交
216c12a33c

+ 9 - 2
client/src/components/Apps/AppForm/AppForm.tsx

@@ -5,6 +5,7 @@ import { App, NewApp } from '../../../interfaces';
 
 import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
 import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
+import Button from '../../UI/Buttons/Button/Button';
 
 interface ComponentProps {
   modalHandler: () => void;
@@ -35,6 +36,12 @@ const AppForm = (props: ComponentProps): JSX.Element => {
         url: props.app.url,
         icon: props.app.icon
       })
+    } else {
+      setFormData({
+        name: '',
+        url: '',
+        icon: ''
+      })
     }
   }, [props.app])
 
@@ -114,8 +121,8 @@ const AppForm = (props: ComponentProps): JSX.Element => {
         </span>
       </InputGroup>
       {!props.app
-        ? <button type="submit">add</button>
-        : <button type="submit">update</button>
+        ? <Button>Add new application</Button>
+        : <Button>Update application</Button>
       }
     </ModalForm>
   )

+ 8 - 5
client/src/components/Apps/AppGrid/AppGrid.module.css

@@ -21,8 +21,11 @@
   }
 }
 
-/* 320px — 480px: Mobile devices.
-481px — 768px: iPads, Tablets.
-769px — 1024px: Small screens, laptops.
-1025px — 1200px: Desktops, large screens.
-1201px and more — Extra large screens, TV. */
+.GridMessage {
+  color: var(--color-primary);
+}
+
+.GridMessage a {
+  color: var(--color-accent);
+  font-weight: 600;
+}

+ 1 - 1
client/src/components/Apps/Apps.tsx

@@ -104,7 +104,7 @@ const Apps = (props: ComponentProps): JSX.Element => {
           : (!isInEdit
               ? props.apps.length > 0
                 ? <AppGrid apps={props.apps} />
-                : <p className={classes.AppsMessage}>You don't have any applications. You can a new one from <Link to='/applications'>/application</Link> menu</p>
+                : <p className={classes.AppsMessage}>You don't have any applications. You can add a new one from <Link to='/applications'>/application</Link> menu</p>
               : <AppTable updateAppHandler={toggleUpdate} />)
         }
       </div>

+ 55 - 18
client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx

@@ -3,16 +3,20 @@ import { connect } from 'react-redux';
 
 import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
 import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
-import { Category, GlobalState, NewBookmark, NewCategory } from '../../../interfaces';
+import { Category, GlobalState, NewBookmark, NewCategory, NewNotification } from '../../../interfaces';
 import { ContentType } from '../Bookmarks';
-import { getCategories, addCategory, addBookmark } from '../../../store/actions';
+import { getCategories, addCategory, addBookmark, updateCategory, createNotification } from '../../../store/actions';
+import Button from '../../UI/Buttons/Button/Button';
 
 interface ComponentProps {
   modalHandler: () => void;
   contentType: ContentType;
   categories: Category[];
+  category?: Category;
   addCategory: (formData: NewCategory) => void;
   addBookmark: (formData: NewBookmark) => void;
+  updateCategory: (id: number, formData: NewCategory) => void;
+  createNotification: (notification: NewNotification) => void;
 }
 
 const BookmarkForm = (props: ComponentProps): JSX.Element => {
@@ -26,24 +30,46 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
     categoryId: -1
   })
 
+  useEffect(() => {
+    if (props.category) {
+      setCategoryName({ name: props.category.name });
+    } else {
+      setCategoryName({ name: '' });
+    }
+  }, [props.category])
+
   const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => {
     e.preventDefault();
-    
-    if (props.contentType === ContentType.category) {
-      props.addCategory(categoryName);
-      setCategoryName({ name: '' });
-    } else if (props.contentType === ContentType.bookmark) {
-      if (formData.categoryId === -1) {
-        alert('select category');
-        return;
+
+    if (!props.category) {
+      // Add new
+      if (props.contentType === ContentType.category) {
+        props.addCategory(categoryName);
+        setCategoryName({ name: '' });
+      } else if (props.contentType === ContentType.bookmark) {
+        if (formData.categoryId === -1) {
+          props.createNotification({
+            title: 'Error',
+            message: 'Please select category'
+          })
+          return;
+        }
+  
+        props.addBookmark(formData);
+        setFormData({
+          name: '',
+          url: '',
+          categoryId: formData.categoryId
+        })
+      }
+    } else {
+      // Update
+      if (props.contentType === ContentType.category) {
+        props.updateCategory(props.category.id, categoryName);
+        setCategoryName({ name: '' });
       }
 
-      props.addBookmark(formData);
-      setFormData({
-        name: '',
-        url: '',
-        categoryId: formData.categoryId
-      })
+      props.modalHandler();
     }
   }
 
@@ -133,7 +159,10 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
           </Fragment>
         )
       }
-      <button type='submit'>add</button>
+      {!props.category
+        ? <Button>Add new category</Button>
+        : <Button>Update category</Button>
+      }
     </ModalForm>
   )
 }
@@ -144,4 +173,12 @@ const mapStateToProps = (state: GlobalState) => {
   }
 }
 
-export default connect(mapStateToProps, { getCategories, addCategory, addBookmark })(BookmarkForm);
+const dispatchMap = {
+  getCategories,
+  addCategory,
+  addBookmark,
+  updateCategory,
+  createNotification
+}
+
+export default connect(mapStateToProps, dispatchMap)(BookmarkForm);

+ 2 - 1
client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx

@@ -13,6 +13,7 @@ interface ComponentProps {
   categories: Category[];
   pinCategory: (category: Category) => void;
   deleteCategory: (id: number) => void;
+  updateCategoryHandler: (category: Category) => void;
 }
 
 const BookmarkTable = (props: ComponentProps): JSX.Element => {
@@ -50,7 +51,7 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
                 </div>
                 <div
                   className={classes.TableAction}
-                  // onClick={() => props.updateAppHandler(app)}
+                  onClick={() => props.updateCategoryHandler(category)}
                   // onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
                   tabIndex={0}>
                   <Icon icon='mdiPencil' />

+ 29 - 3
client/src/components/Bookmarks/Bookmarks.tsx

@@ -32,6 +32,15 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
   const [formContentType, setFormContentType] = useState(ContentType.category);
   const [isInEdit, setIsInEdit] = useState(false);
   const [tableContentType, setTableContentType] = useState(ContentType.category);
+  const [isInUpdate, setIsInUpdate] = useState(false);
+  const [categoryInUpdate, setCategoryInUpdate] = useState<Category>({
+    name: '',
+    id: -1,
+    isPinned: false,
+    bookmarks: [],
+    createdAt: new Date(),
+    updatedAt: new Date()
+  })
 
   useEffect(() => {
     if (props.categories.length === 0) {
@@ -45,6 +54,7 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
 
   const addActionHandler = (contentType: ContentType) => {
     setFormContentType(contentType);
+    setIsInUpdate(false);
     toggleModal();
   }
 
@@ -62,10 +72,21 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
     }
   }
 
+  const goToUpdateMode = (category: Category): void => {
+    setIsInUpdate(true);
+    setCategoryInUpdate(category);
+    toggleModal();
+  }
+
+  let modalForm: JSX.Element;
+
   return (
     <Container>
       <Modal isOpen={modalIsOpen} setIsOpen={toggleModal}>
-        <BookmarkForm modalHandler={toggleModal} contentType={formContentType} />
+        {!isInUpdate
+          ? <BookmarkForm modalHandler={toggleModal} contentType={formContentType} />
+          : <BookmarkForm modalHandler={toggleModal} contentType={formContentType} category={categoryInUpdate} />
+        }
       </Modal>
 
       <Headline
@@ -101,8 +122,13 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
         : (!isInEdit
           ? props.categories.length > 0
             ? <BookmarkGrid categories={props.categories} />
-            : <p className={classes.BookmarksMessage}>You don't have any bookmarks. You can a new one from <Link to='/bookmarks'>/bookmarks</Link> menu</p>
-          : <BookmarkTable contentType={tableContentType} categories={props.categories} />)
+            : <p className={classes.BookmarksMessage}>You don't have any bookmarks. You can add a new one from <Link to='/bookmarks'>/bookmarks</Link> menu</p>
+          : (<BookmarkTable
+              contentType={tableContentType}
+              categories={props.categories}
+              updateCategoryHandler={goToUpdateMode}
+            />)
+          )
       }
     </Container>
   )

+ 14 - 0
client/src/components/UI/Buttons/Button/Button.module.css

@@ -0,0 +1,14 @@
+.Button {
+  padding: 8px 15px;
+  border: 1px solid var(--color-accent);
+  background-color: var(--color-background);
+  color: var(--color-primary);
+  border-radius: 4px;
+}
+
+.Button:hover,
+.Button:focus {
+  cursor: pointer;
+  background-color: var(--color-accent);
+  color: var(--color-background);
+}

+ 11 - 0
client/src/components/UI/Buttons/Button/Button.tsx

@@ -0,0 +1,11 @@
+import classes from './Button.module.css';
+
+interface ComponentProps {
+  children: string;
+}
+
+const Button = (props: ComponentProps): JSX.Element => {
+  return <button className={classes.Button}>{props.children}</button>
+}
+
+export default Button;

+ 3 - 0
client/src/store/actions/actionTypes.ts

@@ -13,6 +13,7 @@ import {
   AddBookmarkAction,
   PinCategoryAction,
   DeleteCategoryAction,
+  UpdateCategoryAction,
   // Notifications
   CreateNotificationAction,
   ClearNotificationAction
@@ -38,6 +39,7 @@ export enum ActionTypes {
   addBookmark = 'ADD_BOOKMARK',
   pinCategory = 'PIN_CATEGORY',
   deleteCategory = 'DELETE_CATEGORY',
+  updateCategory = 'UPDATE_CATEGORY',
   // Notifications
   createNotification = 'CREATE_NOTIFICATION',
   clearNotification = 'CLEAR_NOTIFICATION'
@@ -58,6 +60,7 @@ export type Action =
   AddBookmarkAction |
   PinCategoryAction |
   DeleteCategoryAction |
+  UpdateCategoryAction |
   // Notifications
   CreateNotificationAction |
   ClearNotificationAction;

+ 29 - 0
client/src/store/actions/bookmark.ts

@@ -147,4 +147,33 @@ export const deleteCategory = (id: number) => async (dispatch: Dispatch) => {
   } catch (err) {
     console.log(err);
   }
+}
+
+/**
+ * UPDATE CATEGORY
+ */
+export interface UpdateCategoryAction {
+  type: ActionTypes.updateCategory,
+  payload: Category
+}
+
+export const updateCategory = (id: number, formData: NewCategory) => async (dispatch: Dispatch) => {
+  try {
+    const res = await axios.put<ApiResponse<Category>>(`/api/categories/${id}`, formData);
+
+    dispatch<CreateNotificationAction>({
+      type: ActionTypes.createNotification,
+      payload: {
+        title: 'Success',
+        message: `Category ${formData.name} updated`
+      }
+    })
+
+    dispatch<UpdateCategoryAction>({
+      type: ActionTypes.updateCategory,
+      payload: res.data.data
+    })
+  } catch (err) {
+    console.log(err);
+  }
 }

+ 15 - 0
client/src/store/reducers/bookmark.ts

@@ -78,6 +78,20 @@ const deleteCategory = (state: State, action: Action): State => {
   }
 }
 
+const updateCategory = (state: State, action: Action): State => {
+  const tmpCategories = [...state.categories];
+  const categoryInUpdate = tmpCategories.find((category: Category) => category.id === action.payload.id);
+
+  if (categoryInUpdate) {
+    categoryInUpdate.name = action.payload.name;
+  }
+
+  return {
+    ...state,
+    categories: tmpCategories
+  }
+}
+
 const bookmarkReducer = (state = initialState, action: Action) => {
   switch (action.type) {
     case ActionTypes.getCategories: return getCategories(state, action);
@@ -86,6 +100,7 @@ const bookmarkReducer = (state = initialState, action: Action) => {
     case ActionTypes.addBookmark: return addBookmark(state, action);
     case ActionTypes.pinCategory: return pinCategory(state, action);
     case ActionTypes.deleteCategory: return deleteCategory(state, action);
+    case ActionTypes.updateCategory: return updateCategory(state, action);
     default: return state;
   }
 }