Quellcode durchsuchen

Split categories and bookmarks forms into separate files. Added visibility functionality to categories and bookmarks

Paweł Malak vor 3 Jahren
Ursprung
Commit
d83e3056c6

+ 11 - 13
client/src/components/Apps/AppForm/AppForm.tsx

@@ -14,7 +14,7 @@ interface Props {
   app?: App;
 }
 
-export const AppForm = (props: Props): JSX.Element => {
+export const AppForm = ({ app, modalHandler }: Props): JSX.Element => {
   const dispatch = useDispatch();
   const { addApp, updateApp } = bindActionCreators(actionCreators, dispatch);
 
@@ -23,14 +23,14 @@ export const AppForm = (props: Props): JSX.Element => {
   const [formData, setFormData] = useState<NewApp>(newAppTemplate);
 
   useEffect(() => {
-    if (props.app) {
+    if (app) {
       setFormData({
-        ...props.app,
+        ...app,
       });
     } else {
       setFormData(newAppTemplate);
     }
-  }, [props.app]);
+  }, [app]);
 
   const inputChangeHandler = (
     e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
@@ -61,11 +61,12 @@ export const AppForm = (props: Props): JSX.Element => {
       }
       data.append('name', formData.name);
       data.append('url', formData.url);
+      data.append('isPublic', `${formData.isPublic}`);
 
       return data;
     };
 
-    if (!props.app) {
+    if (!app) {
       if (customIcon) {
         const data = createFormData();
         addApp(data);
@@ -75,10 +76,10 @@ export const AppForm = (props: Props): JSX.Element => {
     } else {
       if (customIcon) {
         const data = createFormData();
-        updateApp(props.app.id, data);
+        updateApp(app.id, data);
       } else {
-        updateApp(props.app.id, formData);
-        props.modalHandler();
+        updateApp(app.id, formData);
+        modalHandler();
       }
     }
 
@@ -86,10 +87,7 @@ export const AppForm = (props: Props): JSX.Element => {
   };
 
   return (
-    <ModalForm
-      modalHandler={props.modalHandler}
-      formHandler={formSubmitHandler}
-    >
+    <ModalForm modalHandler={modalHandler} formHandler={formSubmitHandler}>
       {/* NAME */}
       <InputGroup>
         <label htmlFor="name">App Name</label>
@@ -184,7 +182,7 @@ export const AppForm = (props: Props): JSX.Element => {
         </select>
       </InputGroup>
 
-      {!props.app ? (
+      {!app ? (
         <Button>Add new application</Button>
       ) : (
         <Button>Update application</Button>

+ 0 - 318
client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx

@@ -1,318 +0,0 @@
-// React
-import {
-  useState,
-  SyntheticEvent,
-  Fragment,
-  ChangeEvent,
-  useEffect,
-} from 'react';
-
-// Redux
-import { useDispatch, useSelector } from 'react-redux';
-
-// Typescript
-import {
-  Bookmark,
-  Category,
-  NewBookmark,
-  NewCategory,
-} from '../../../interfaces';
-import { ContentType } from '../Bookmarks';
-
-// UI
-import { ModalForm, InputGroup, Button } from '../../UI';
-
-// CSS
-import classes from './BookmarkForm.module.css';
-import { newBookmarkTemplate, newCategoryTemplate } from '../../../utility';
-import { State } from '../../../store/reducers';
-import { bindActionCreators } from 'redux';
-import { actionCreators } from '../../../store';
-
-interface Props {
-  modalHandler: () => void;
-  contentType: ContentType;
-  category?: Category;
-  bookmark?: Bookmark;
-}
-
-export const BookmarkForm = (props: Props): JSX.Element => {
-  const { categories } = useSelector((state: State) => state.bookmarks);
-
-  const dispatch = useDispatch();
-  const {
-    addCategory,
-    addBookmark,
-    updateCategory,
-    updateBookmark,
-    createNotification,
-  } = bindActionCreators(actionCreators, dispatch);
-
-  const [useCustomIcon, toggleUseCustomIcon] = useState<boolean>(false);
-  const [customIcon, setCustomIcon] = useState<File | null>(null);
-  const [categoryName, setCategoryName] =
-    useState<NewCategory>(newCategoryTemplate);
-
-  const [formData, setFormData] = useState<NewBookmark>(newBookmarkTemplate);
-
-  // Load category data if provided for editing
-  useEffect(() => {
-    if (props.category) {
-      setCategoryName({ ...props.category });
-    } else {
-      setCategoryName(newCategoryTemplate);
-    }
-  }, [props.category]);
-
-  // Load bookmark data if provided for editing
-  useEffect(() => {
-    if (props.bookmark) {
-      setFormData({ ...props.bookmark });
-    } else {
-      setFormData(newBookmarkTemplate);
-    }
-  }, [props.bookmark]);
-
-  const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => {
-    e.preventDefault();
-
-    const createFormData = (): FormData => {
-      const data = new FormData();
-      if (customIcon) {
-        data.append('icon', customIcon);
-      }
-      data.append('name', formData.name);
-      data.append('url', formData.url);
-      data.append('categoryId', `${formData.categoryId}`);
-
-      return data;
-    };
-
-    if (!props.category && !props.bookmark) {
-      // Add new
-      if (props.contentType === ContentType.category) {
-        // Add category
-        addCategory(categoryName);
-        setCategoryName(newCategoryTemplate);
-      } else if (props.contentType === ContentType.bookmark) {
-        // Add bookmark
-        if (formData.categoryId === -1) {
-          createNotification({
-            title: 'Error',
-            message: 'Please select category',
-          });
-          return;
-        }
-
-        if (customIcon) {
-          const data = createFormData();
-          addBookmark(data);
-        } else {
-          addBookmark(formData);
-        }
-
-        setFormData({
-          ...newBookmarkTemplate,
-          categoryId: formData.categoryId,
-        });
-
-        // setCustomIcon(null);
-      }
-    } else {
-      // Update
-      if (props.contentType === ContentType.category && props.category) {
-        // Update category
-        updateCategory(props.category.id, categoryName);
-        setCategoryName(newCategoryTemplate);
-      } else if (props.contentType === ContentType.bookmark && props.bookmark) {
-        // Update bookmark
-        if (customIcon) {
-          const data = createFormData();
-          updateBookmark(props.bookmark.id, data, {
-            prev: props.bookmark.categoryId,
-            curr: formData.categoryId,
-          });
-        } else {
-          updateBookmark(props.bookmark.id, formData, {
-            prev: props.bookmark.categoryId,
-            curr: formData.categoryId,
-          });
-        }
-
-        setFormData(newBookmarkTemplate);
-
-        setCustomIcon(null);
-      }
-
-      props.modalHandler();
-    }
-  };
-
-  const inputChangeHandler = (e: ChangeEvent<HTMLInputElement>): void => {
-    setFormData({
-      ...formData,
-      [e.target.name]: e.target.value,
-    });
-  };
-
-  const selectChangeHandler = (e: ChangeEvent<HTMLSelectElement>): void => {
-    setFormData({
-      ...formData,
-      categoryId: parseInt(e.target.value),
-    });
-  };
-
-  const fileChangeHandler = (e: ChangeEvent<HTMLInputElement>): void => {
-    if (e.target.files) {
-      setCustomIcon(e.target.files[0]);
-    }
-  };
-
-  let button = <Button>Submit</Button>;
-
-  if (!props.category && !props.bookmark) {
-    if (props.contentType === ContentType.category) {
-      button = <Button>Add new category</Button>;
-    } else {
-      button = <Button>Add new bookmark</Button>;
-    }
-  } else if (props.category) {
-    button = <Button>Update category</Button>;
-  } else if (props.bookmark) {
-    button = <Button>Update bookmark</Button>;
-  }
-
-  return (
-    <ModalForm
-      modalHandler={props.modalHandler}
-      formHandler={formSubmitHandler}
-    >
-      {props.contentType === ContentType.category ? (
-        <Fragment>
-          <InputGroup>
-            <label htmlFor="categoryName">Category Name</label>
-            <input
-              type="text"
-              name="categoryName"
-              id="categoryName"
-              placeholder="Social Media"
-              required
-              value={categoryName.name}
-              onChange={(e) =>
-                setCategoryName({ name: e.target.value, isPublic: !!!!!false })
-              }
-            />
-          </InputGroup>
-        </Fragment>
-      ) : (
-        <Fragment>
-          <InputGroup>
-            <label htmlFor="name">Bookmark Name</label>
-            <input
-              type="text"
-              name="name"
-              id="name"
-              placeholder="Reddit"
-              required
-              value={formData.name}
-              onChange={(e) => inputChangeHandler(e)}
-            />
-          </InputGroup>
-
-          <InputGroup>
-            <label htmlFor="url">Bookmark URL</label>
-            <input
-              type="text"
-              name="url"
-              id="url"
-              placeholder="reddit.com"
-              required
-              value={formData.url}
-              onChange={(e) => inputChangeHandler(e)}
-            />
-            <span>
-              <a
-                href="https://github.com/pawelmalak/flame#supported-url-formats-for-applications-and-bookmarks"
-                target="_blank"
-                rel="noreferrer"
-              >
-                {' '}
-                Check supported URL formats
-              </a>
-            </span>
-          </InputGroup>
-
-          <InputGroup>
-            <label htmlFor="categoryId">Bookmark Category</label>
-            <select
-              name="categoryId"
-              id="categoryId"
-              required
-              onChange={(e) => selectChangeHandler(e)}
-              value={formData.categoryId}
-            >
-              <option value={-1}>Select category</option>
-              {categories.map((category: Category): JSX.Element => {
-                return (
-                  <option key={category.id} value={category.id}>
-                    {category.name}
-                  </option>
-                );
-              })}
-            </select>
-          </InputGroup>
-
-          {!useCustomIcon ? (
-            // mdi
-            <InputGroup>
-              <label htmlFor="icon">Bookmark Icon (optional)</label>
-              <input
-                type="text"
-                name="icon"
-                id="icon"
-                placeholder="book-open-outline"
-                value={formData.icon}
-                onChange={(e) => inputChangeHandler(e)}
-              />
-              <span>
-                Use icon name from MDI.
-                <a href="https://materialdesignicons.com/" target="blank">
-                  {' '}
-                  Click here for reference
-                </a>
-              </span>
-              <span
-                onClick={() => toggleUseCustomIcon(!useCustomIcon)}
-                className={classes.Switch}
-              >
-                Switch to custom icon upload
-              </span>
-            </InputGroup>
-          ) : (
-            // custom
-            <InputGroup>
-              <label htmlFor="icon">Bookmark Icon (optional)</label>
-              <input
-                type="file"
-                name="icon"
-                id="icon"
-                onChange={(e) => fileChangeHandler(e)}
-                accept=".jpg,.jpeg,.png,.svg"
-              />
-              <span
-                onClick={() => {
-                  setCustomIcon(null);
-                  toggleUseCustomIcon(!useCustomIcon);
-                }}
-                className={classes.Switch}
-              >
-                Switch to MDI
-              </span>
-            </InputGroup>
-          )}
-        </Fragment>
-      )}
-      {button}
-    </ModalForm>
-  );
-};

+ 14 - 3
client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx

@@ -125,7 +125,10 @@ export const BookmarkTable = (props: Props): JSX.Element => {
         <DragDropContext onDragEnd={dragEndHanlder}>
           <Droppable droppableId="categories">
             {(provided) => (
-              <Table headers={['Name', 'Actions']} innerRef={provided.innerRef}>
+              <Table
+                headers={['Name', 'Visibility', 'Actions']}
+                innerRef={provided.innerRef}
+              >
                 {localCategories.map(
                   (category: Category, index): JSX.Element => {
                     return (
@@ -150,7 +153,12 @@ export const BookmarkTable = (props: Props): JSX.Element => {
                               ref={provided.innerRef}
                               style={style}
                             >
-                              <td>{category.name}</td>
+                              <td style={{ width: '300px' }}>
+                                {category.name}
+                              </td>
+                              <td style={{ width: '300px' }}>
+                                {category.isPublic ? 'Visible' : 'Hidden'}
+                              </td>
                               {!snapshot.isDragging && (
                                 <td className={classes.TableActions}>
                                   <div
@@ -226,7 +234,9 @@ export const BookmarkTable = (props: Props): JSX.Element => {
     });
 
     return (
-      <Table headers={['Name', 'URL', 'Icon', 'Category', 'Actions']}>
+      <Table
+        headers={['Name', 'URL', 'Icon', 'Visibility', 'Category', 'Actions']}
+      >
         {bookmarks.map(
           (bookmark: { bookmark: Bookmark; categoryName: string }) => {
             return (
@@ -234,6 +244,7 @@ export const BookmarkTable = (props: Props): JSX.Element => {
                 <td>{bookmark.bookmark.name}</td>
                 <td>{bookmark.bookmark.url}</td>
                 <td>{bookmark.bookmark.icon}</td>
+                <td>{bookmark.bookmark.isPublic ? 'Visible' : 'Hidden'}</td>
                 <td>{bookmark.categoryName}</td>
                 <td className={classes.TableActions}>
                   <div

+ 8 - 19
client/src/components/Bookmarks/Bookmarks.tsx

@@ -18,8 +18,8 @@ import { Container, Headline, ActionButton, Spinner, Modal } from '../UI';
 
 // Components
 import { BookmarkGrid } from './BookmarkGrid/BookmarkGrid';
-import { BookmarkForm } from './BookmarkForm/BookmarkForm';
 import { BookmarkTable } from './BookmarkTable/BookmarkTable';
+import { Form } from './Form/Form';
 
 // Utils
 import { bookmarkTemplate, categoryTemplate } from '../../utility';
@@ -98,24 +98,13 @@ export const Bookmarks = (props: Props): JSX.Element => {
   return (
     <Container>
       <Modal isOpen={modalIsOpen} setIsOpen={toggleModal}>
-        {!isInUpdate ? (
-          <BookmarkForm
-            modalHandler={toggleModal}
-            contentType={formContentType}
-          />
-        ) : formContentType === ContentType.category ? (
-          <BookmarkForm
-            modalHandler={toggleModal}
-            contentType={formContentType}
-            category={categoryInUpdate}
-          />
-        ) : (
-          <BookmarkForm
-            modalHandler={toggleModal}
-            contentType={formContentType}
-            bookmark={bookmarkInUpdate}
-          />
-        )}
+        <Form
+          modalHandler={toggleModal}
+          contentType={formContentType}
+          inUpdate={isInUpdate}
+          category={categoryInUpdate}
+          bookmark={bookmarkInUpdate}
+        />
       </Modal>
 
       <Headline title="All Bookmarks" subtitle={<Link to="/">Go back</Link>} />

+ 260 - 0
client/src/components/Bookmarks/Form/BookmarksForm.tsx

@@ -0,0 +1,260 @@
+import { useState, ChangeEvent, useEffect, FormEvent } from 'react';
+
+// Redux
+import { useDispatch, useSelector } from 'react-redux';
+import { State } from '../../../store/reducers';
+import { bindActionCreators } from 'redux';
+import { actionCreators } from '../../../store';
+
+// Typescript
+import { Bookmark, Category, NewBookmark } from '../../../interfaces';
+
+// UI
+import { ModalForm, InputGroup, Button } from '../../UI';
+
+// CSS
+import classes from './Form.module.css';
+
+// Utils
+import { inputHandler, newBookmarkTemplate } from '../../../utility';
+
+interface Props {
+  modalHandler: () => void;
+  bookmark?: Bookmark;
+}
+
+export const BookmarksForm = ({
+  bookmark,
+  modalHandler,
+}: Props): JSX.Element => {
+  const { categories } = useSelector((state: State) => state.bookmarks);
+
+  const dispatch = useDispatch();
+  const { addBookmark, updateBookmark, createNotification } =
+    bindActionCreators(actionCreators, dispatch);
+
+  const [useCustomIcon, toggleUseCustomIcon] = useState<boolean>(false);
+  const [customIcon, setCustomIcon] = useState<File | null>(null);
+
+  const [formData, setFormData] = useState<NewBookmark>(newBookmarkTemplate);
+
+  // Load bookmark data if provided for editing
+  useEffect(() => {
+    if (bookmark) {
+      setFormData({ ...bookmark });
+    } else {
+      setFormData(newBookmarkTemplate);
+    }
+  }, [bookmark]);
+
+  const inputChangeHandler = (
+    e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
+    options?: { isNumber?: boolean; isBool?: boolean }
+  ) => {
+    inputHandler<NewBookmark>({
+      e,
+      options,
+      setStateHandler: setFormData,
+      state: formData,
+    });
+  };
+
+  const fileChangeHandler = (e: ChangeEvent<HTMLInputElement>): void => {
+    if (e.target.files) {
+      setCustomIcon(e.target.files[0]);
+    }
+  };
+
+  // Bookmarks form handler
+  const formSubmitHandler = (e: FormEvent): void => {
+    e.preventDefault();
+
+    const createFormData = (): FormData => {
+      const data = new FormData();
+      if (customIcon) {
+        data.append('icon', customIcon);
+      }
+      data.append('name', formData.name);
+      data.append('url', formData.url);
+      data.append('categoryId', `${formData.categoryId}`);
+      data.append('isPublic', `${formData.isPublic}`);
+
+      return data;
+    };
+
+    const checkCategory = (): boolean => {
+      if (formData.categoryId < 0) {
+        createNotification({
+          title: 'Error',
+          message: 'Please select category',
+        });
+
+        return false;
+      }
+
+      return true;
+    };
+
+    if (!bookmark) {
+      // add new bookmark
+      if (!checkCategory()) return;
+
+      if (formData.categoryId < 0) {
+        createNotification({
+          title: 'Error',
+          message: 'Please select category',
+        });
+        return;
+      }
+
+      if (customIcon) {
+        const data = createFormData();
+        addBookmark(data);
+      } else {
+        addBookmark(formData);
+      }
+
+      setFormData({
+        ...newBookmarkTemplate,
+        categoryId: formData.categoryId,
+        isPublic: formData.isPublic,
+      });
+    } else {
+      // update
+      if (!checkCategory()) return;
+
+      if (customIcon) {
+        const data = createFormData();
+        updateBookmark(bookmark.id, data, {
+          prev: bookmark.categoryId,
+          curr: formData.categoryId,
+        });
+      } else {
+        updateBookmark(bookmark.id, formData, {
+          prev: bookmark.categoryId,
+          curr: formData.categoryId,
+        });
+      }
+
+      modalHandler();
+
+      setFormData(newBookmarkTemplate);
+
+      setCustomIcon(null);
+    }
+  };
+
+  return (
+    <ModalForm modalHandler={modalHandler} formHandler={formSubmitHandler}>
+      <InputGroup>
+        <label htmlFor="name">Bookmark Name</label>
+        <input
+          type="text"
+          name="name"
+          id="name"
+          placeholder="Reddit"
+          required
+          value={formData.name}
+          onChange={(e) => inputChangeHandler(e)}
+        />
+      </InputGroup>
+
+      <InputGroup>
+        <label htmlFor="url">Bookmark URL</label>
+        <input
+          type="text"
+          name="url"
+          id="url"
+          placeholder="reddit.com"
+          required
+          value={formData.url}
+          onChange={(e) => inputChangeHandler(e)}
+        />
+      </InputGroup>
+
+      <InputGroup>
+        <label htmlFor="categoryId">Bookmark Category</label>
+        <select
+          name="categoryId"
+          id="categoryId"
+          required
+          onChange={(e) => inputChangeHandler(e, { isNumber: true })}
+          value={formData.categoryId}
+        >
+          <option value={-1}>Select category</option>
+          {categories.map((category: Category): JSX.Element => {
+            return (
+              <option key={category.id} value={category.id}>
+                {category.name}
+              </option>
+            );
+          })}
+        </select>
+      </InputGroup>
+
+      {!useCustomIcon ? (
+        // mdi
+        <InputGroup>
+          <label htmlFor="icon">Bookmark Icon (optional)</label>
+          <input
+            type="text"
+            name="icon"
+            id="icon"
+            placeholder="book-open-outline"
+            value={formData.icon}
+            onChange={(e) => inputChangeHandler(e)}
+          />
+          <span>
+            Use icon name from MDI.
+            <a href="https://materialdesignicons.com/" target="blank">
+              {' '}
+              Click here for reference
+            </a>
+          </span>
+          <span
+            onClick={() => toggleUseCustomIcon(!useCustomIcon)}
+            className={classes.Switch}
+          >
+            Switch to custom icon upload
+          </span>
+        </InputGroup>
+      ) : (
+        // custom
+        <InputGroup>
+          <label htmlFor="icon">Bookmark Icon (optional)</label>
+          <input
+            type="file"
+            name="icon"
+            id="icon"
+            onChange={(e) => fileChangeHandler(e)}
+            accept=".jpg,.jpeg,.png,.svg"
+          />
+          <span
+            onClick={() => {
+              setCustomIcon(null);
+              toggleUseCustomIcon(!useCustomIcon);
+            }}
+            className={classes.Switch}
+          >
+            Switch to MDI
+          </span>
+        </InputGroup>
+      )}
+
+      <InputGroup>
+        <label htmlFor="isPublic">Bookmark visibility</label>
+        <select
+          id="isPublic"
+          name="isPublic"
+          value={formData.isPublic ? 1 : 0}
+          onChange={(e) => inputChangeHandler(e, { isBool: true })}
+        >
+          <option value={1}>Visible (anyone can access it)</option>
+          <option value={0}>Hidden (authentication required)</option>
+        </select>
+      </InputGroup>
+
+      <Button>{bookmark ? 'Update bookmark' : 'Add new bookmark'}</Button>
+    </ModalForm>
+  );
+};

+ 100 - 0
client/src/components/Bookmarks/Form/CategoryForm.tsx

@@ -0,0 +1,100 @@
+import { ChangeEvent, FormEvent, useEffect, useState } from 'react';
+
+// Redux
+import { useDispatch } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import { actionCreators } from '../../../store';
+
+// Typescript
+import { Category, NewCategory } from '../../../interfaces';
+
+// UI
+import { ModalForm, InputGroup, Button } from '../../UI';
+
+// Utils
+import { inputHandler, newCategoryTemplate } from '../../../utility';
+
+interface Props {
+  modalHandler: () => void;
+  category?: Category;
+}
+
+export const CategoryForm = ({
+  category,
+  modalHandler,
+}: Props): JSX.Element => {
+  const dispatch = useDispatch();
+  const { addCategory, updateCategory } = bindActionCreators(
+    actionCreators,
+    dispatch
+  );
+
+  const [formData, setFormData] = useState<NewCategory>(newCategoryTemplate);
+
+  // Load category data if provided for editing
+  useEffect(() => {
+    if (category) {
+      setFormData({ ...category });
+    } else {
+      setFormData(newCategoryTemplate);
+    }
+  }, [category]);
+
+  const inputChangeHandler = (
+    e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
+    options?: { isNumber?: boolean; isBool?: boolean }
+  ) => {
+    inputHandler<NewCategory>({
+      e,
+      options,
+      setStateHandler: setFormData,
+      state: formData,
+    });
+  };
+
+  // Category form handler
+  const formSubmitHandler = (e: FormEvent): void => {
+    e.preventDefault();
+
+    if (!category) {
+      addCategory(formData);
+    } else {
+      updateCategory(category.id, formData);
+
+      setFormData(newCategoryTemplate);
+      modalHandler();
+    }
+  };
+
+  return (
+    <ModalForm modalHandler={modalHandler} formHandler={formSubmitHandler}>
+      <InputGroup>
+        <label htmlFor="name">Category Name</label>
+        <input
+          type="text"
+          name="name"
+          id="name"
+          placeholder="Social Media"
+          required
+          value={formData.name}
+          onChange={(e) => inputChangeHandler(e)}
+        />
+      </InputGroup>
+
+      <InputGroup>
+        <label htmlFor="isPublic">Category visibility</label>
+        <select
+          id="isPublic"
+          name="isPublic"
+          value={formData.isPublic ? 1 : 0}
+          onChange={(e) => inputChangeHandler(e, { isBool: true })}
+        >
+          <option value={1}>Visible (anyone can access it)</option>
+          <option value={0}>Hidden (authentication required)</option>
+        </select>
+      </InputGroup>
+
+      <Button>{category ? 'Update category' : 'Add new category'}</Button>
+    </ModalForm>
+  );
+};

+ 0 - 0
client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css → client/src/components/Bookmarks/Form/Form.module.css


+ 44 - 0
client/src/components/Bookmarks/Form/Form.tsx

@@ -0,0 +1,44 @@
+// Typescript
+import { Bookmark, Category } from '../../../interfaces';
+import { ContentType } from '../Bookmarks';
+
+// Utils
+import { CategoryForm } from './CategoryForm';
+import { BookmarksForm } from './BookmarksForm';
+import { Fragment } from 'react';
+
+interface Props {
+  modalHandler: () => void;
+  contentType: ContentType;
+  inUpdate?: boolean;
+  category?: Category;
+  bookmark?: Bookmark;
+}
+
+export const Form = (props: Props): JSX.Element => {
+  const { modalHandler, contentType, inUpdate, category, bookmark } = props;
+
+  return (
+    <Fragment>
+      {!inUpdate ? (
+        // form: add new
+        <Fragment>
+          {contentType === ContentType.category ? (
+            <CategoryForm modalHandler={modalHandler} />
+          ) : (
+            <BookmarksForm modalHandler={modalHandler} />
+          )}
+        </Fragment>
+      ) : (
+        // form: update
+        <Fragment>
+          {contentType === ContentType.category ? (
+            <CategoryForm modalHandler={modalHandler} category={category} />
+          ) : (
+            <BookmarksForm modalHandler={modalHandler} bookmark={bookmark} />
+          )}
+        </Fragment>
+      )}
+    </Fragment>
+  );
+};

+ 2 - 1
client/src/components/UI/Buttons/Button/Button.tsx

@@ -1,7 +1,8 @@
+import { ReactNode } from 'react';
 import classes from './Button.module.css';
 
 interface Props {
-  children: string;
+  children: ReactNode;
   click?: any;
 }
 

+ 3 - 7
client/src/interfaces/Bookmark.ts

@@ -1,15 +1,11 @@
 import { Model } from '.';
 
-export interface Bookmark extends Model {
+export interface NewBookmark {
   name: string;
   url: string;
   categoryId: number;
   icon: string;
+  isPublic: boolean;
 }
 
-export interface NewBookmark {
-  name: string;
-  url: string;
-  categoryId: number;
-  icon: string;
-}
+export interface Bookmark extends Model, NewBookmark {}

+ 5 - 5
client/src/interfaces/Category.ts

@@ -1,12 +1,12 @@
 import { Model, Bookmark } from '.';
 
-export interface Category extends Model {
+export interface NewCategory {
   name: string;
+  isPublic: boolean;
+}
+
+export interface Category extends Model, NewCategory {
   isPinned: boolean;
   orderId: number;
   bookmarks: Bookmark[];
 }
-
-export interface NewCategory {
-  name: string;
-}

+ 1 - 1
client/src/utility/checkVersion.ts

@@ -1,6 +1,6 @@
 import axios from 'axios';
 import { store } from '../store/store';
-import { createNotification } from '../store/actions';
+import { createNotification } from '../store/action-creators';
 
 export const checkVersion = async (isForced: boolean = false) => {
   try {

+ 1 - 1
client/src/utility/templateObjects/appTemplate.ts

@@ -11,7 +11,7 @@ export const appTemplate: App = {
   ...newAppTemplate,
   isPinned: false,
   orderId: 0,
-  id: 0,
+  id: -1,
   createdAt: new Date(),
   updatedAt: new Date(),
 };

+ 16 - 0
client/src/utility/templateObjects/bookmarkTemplate.ts

@@ -0,0 +1,16 @@
+import { Bookmark, NewBookmark } from '../../interfaces';
+
+export const newBookmarkTemplate: NewBookmark = {
+  name: '',
+  url: '',
+  categoryId: -1,
+  icon: '',
+  isPublic: true,
+};
+
+export const bookmarkTemplate: Bookmark = {
+  ...newBookmarkTemplate,
+  id: -1,
+  createdAt: new Date(),
+  updatedAt: new Date(),
+};

+ 16 - 0
client/src/utility/templateObjects/categoryTemplate.ts

@@ -0,0 +1,16 @@
+import { Category, NewCategory } from '../../interfaces';
+
+export const newCategoryTemplate: NewCategory = {
+  name: '',
+  isPublic: true,
+};
+
+export const categoryTemplate: Category = {
+  ...newCategoryTemplate,
+  id: -1,
+  isPinned: false,
+  orderId: 0,
+  bookmarks: [],
+  createdAt: new Date(),
+  updatedAt: new Date(),
+};

+ 2 - 0
client/src/utility/templateObjects/index.ts

@@ -1,3 +1,5 @@
 export * from './configTemplate';
 export * from './settingsTemplate';
 export * from './appTemplate';
+export * from './categoryTemplate';
+export * from './bookmarkTemplate';