From d83e3056c60ef6150d42b5b7f8c9a5c211cf6dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Tue, 9 Nov 2021 23:40:58 +0100 Subject: [PATCH] Split categories and bookmarks forms into separate files. Added visibility functionality to categories and bookmarks --- .../src/components/Apps/AppForm/AppForm.tsx | 24 +- .../Bookmarks/BookmarkForm/BookmarkForm.tsx | 318 ------------------ .../Bookmarks/BookmarkTable/BookmarkTable.tsx | 17 +- client/src/components/Bookmarks/Bookmarks.tsx | 27 +- .../Bookmarks/Form/BookmarksForm.tsx | 260 ++++++++++++++ .../Bookmarks/Form/CategoryForm.tsx | 100 ++++++ .../Form.module.css} | 0 client/src/components/Bookmarks/Form/Form.tsx | 44 +++ .../components/UI/Buttons/Button/Button.tsx | 3 +- client/src/interfaces/Bookmark.ts | 12 +- client/src/interfaces/Category.ts | 10 +- client/src/utility/checkVersion.ts | 2 +- .../utility/templateObjects/appTemplate.ts | 2 +- .../templateObjects/bookmarkTemplate.ts | 16 + .../templateObjects/categoryTemplate.ts | 16 + client/src/utility/templateObjects/index.ts | 2 + 16 files changed, 484 insertions(+), 369 deletions(-) delete mode 100644 client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx create mode 100644 client/src/components/Bookmarks/Form/BookmarksForm.tsx create mode 100644 client/src/components/Bookmarks/Form/CategoryForm.tsx rename client/src/components/Bookmarks/{BookmarkForm/BookmarkForm.module.css => Form/Form.module.css} (100%) create mode 100644 client/src/components/Bookmarks/Form/Form.tsx create mode 100644 client/src/utility/templateObjects/bookmarkTemplate.ts create mode 100644 client/src/utility/templateObjects/categoryTemplate.ts diff --git a/client/src/components/Apps/AppForm/AppForm.tsx b/client/src/components/Apps/AppForm/AppForm.tsx index 8c11253..5c3125f 100644 --- a/client/src/components/Apps/AppForm/AppForm.tsx +++ b/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(newAppTemplate); useEffect(() => { - if (props.app) { + if (app) { setFormData({ - ...props.app, + ...app, }); } else { setFormData(newAppTemplate); } - }, [props.app]); + }, [app]); const inputChangeHandler = ( e: ChangeEvent, @@ -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 ( - + {/* NAME */} @@ -184,7 +182,7 @@ export const AppForm = (props: Props): JSX.Element => { - {!props.app ? ( + {!app ? ( ) : ( diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx deleted file mode 100644 index 848848b..0000000 --- a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx +++ /dev/null @@ -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(false); - const [customIcon, setCustomIcon] = useState(null); - const [categoryName, setCategoryName] = - useState(newCategoryTemplate); - - const [formData, setFormData] = useState(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): 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): void => { - setFormData({ - ...formData, - [e.target.name]: e.target.value, - }); - }; - - const selectChangeHandler = (e: ChangeEvent): void => { - setFormData({ - ...formData, - categoryId: parseInt(e.target.value), - }); - }; - - const fileChangeHandler = (e: ChangeEvent): void => { - if (e.target.files) { - setCustomIcon(e.target.files[0]); - } - }; - - let button = ; - - if (!props.category && !props.bookmark) { - if (props.contentType === ContentType.category) { - button = ; - } else { - button = ; - } - } else if (props.category) { - button = ; - } else if (props.bookmark) { - button = ; - } - - return ( - - {props.contentType === ContentType.category ? ( - - - - - setCategoryName({ name: e.target.value, isPublic: !!!!!false }) - } - /> - - - ) : ( - - - - inputChangeHandler(e)} - /> - - - - - inputChangeHandler(e)} - /> - - - {' '} - Check supported URL formats - - - - - - - - - - {!useCustomIcon ? ( - // mdi - - - inputChangeHandler(e)} - /> - - Use icon name from MDI. - - {' '} - Click here for reference - - - toggleUseCustomIcon(!useCustomIcon)} - className={classes.Switch} - > - Switch to custom icon upload - - - ) : ( - // custom - - - fileChangeHandler(e)} - accept=".jpg,.jpeg,.png,.svg" - /> - { - setCustomIcon(null); - toggleUseCustomIcon(!useCustomIcon); - }} - className={classes.Switch} - > - Switch to MDI - - - )} - - )} - {button} - - ); -}; diff --git a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx index 7fd012a..2cc4878 100644 --- a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx +++ b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx @@ -125,7 +125,10 @@ export const BookmarkTable = (props: Props): JSX.Element => { {(provided) => ( - +
{localCategories.map( (category: Category, index): JSX.Element => { return ( @@ -150,7 +153,12 @@ export const BookmarkTable = (props: Props): JSX.Element => { ref={provided.innerRef} style={style} > - + + {!snapshot.isDragging && (
{category.name} + {category.name} + + {category.isPublic ? 'Visible' : 'Hidden'} +
{ }); return ( - +
{bookmarks.map( (bookmark: { bookmark: Bookmark; categoryName: string }) => { return ( @@ -234,6 +244,7 @@ export const BookmarkTable = (props: Props): JSX.Element => { +
{bookmark.bookmark.name} {bookmark.bookmark.url} {bookmark.bookmark.icon}{bookmark.bookmark.isPublic ? 'Visible' : 'Hidden'} {bookmark.categoryName}
{ return ( - {!isInUpdate ? ( - - ) : formContentType === ContentType.category ? ( - - ) : ( - - )} +
Go back} /> diff --git a/client/src/components/Bookmarks/Form/BookmarksForm.tsx b/client/src/components/Bookmarks/Form/BookmarksForm.tsx new file mode 100644 index 0000000..8a19fbd --- /dev/null +++ b/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(false); + const [customIcon, setCustomIcon] = useState(null); + + const [formData, setFormData] = useState(newBookmarkTemplate); + + // Load bookmark data if provided for editing + useEffect(() => { + if (bookmark) { + setFormData({ ...bookmark }); + } else { + setFormData(newBookmarkTemplate); + } + }, [bookmark]); + + const inputChangeHandler = ( + e: ChangeEvent, + options?: { isNumber?: boolean; isBool?: boolean } + ) => { + inputHandler({ + e, + options, + setStateHandler: setFormData, + state: formData, + }); + }; + + const fileChangeHandler = (e: ChangeEvent): 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 ( + + + + inputChangeHandler(e)} + /> + + + + + inputChangeHandler(e)} + /> + + + + + + + + {!useCustomIcon ? ( + // mdi + + + inputChangeHandler(e)} + /> + + Use icon name from MDI. + + {' '} + Click here for reference + + + toggleUseCustomIcon(!useCustomIcon)} + className={classes.Switch} + > + Switch to custom icon upload + + + ) : ( + // custom + + + fileChangeHandler(e)} + accept=".jpg,.jpeg,.png,.svg" + /> + { + setCustomIcon(null); + toggleUseCustomIcon(!useCustomIcon); + }} + className={classes.Switch} + > + Switch to MDI + + + )} + + + + + + + + + ); +}; diff --git a/client/src/components/Bookmarks/Form/CategoryForm.tsx b/client/src/components/Bookmarks/Form/CategoryForm.tsx new file mode 100644 index 0000000..3daa68f --- /dev/null +++ b/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(newCategoryTemplate); + + // Load category data if provided for editing + useEffect(() => { + if (category) { + setFormData({ ...category }); + } else { + setFormData(newCategoryTemplate); + } + }, [category]); + + const inputChangeHandler = ( + e: ChangeEvent, + options?: { isNumber?: boolean; isBool?: boolean } + ) => { + inputHandler({ + 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 ( + + + + inputChangeHandler(e)} + /> + + + + + + + + + + ); +}; diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css b/client/src/components/Bookmarks/Form/Form.module.css similarity index 100% rename from client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css rename to client/src/components/Bookmarks/Form/Form.module.css diff --git a/client/src/components/Bookmarks/Form/Form.tsx b/client/src/components/Bookmarks/Form/Form.tsx new file mode 100644 index 0000000..41ed1bb --- /dev/null +++ b/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 ( + + {!inUpdate ? ( + // form: add new + + {contentType === ContentType.category ? ( + + ) : ( + + )} + + ) : ( + // form: update + + {contentType === ContentType.category ? ( + + ) : ( + + )} + + )} + + ); +}; diff --git a/client/src/components/UI/Buttons/Button/Button.tsx b/client/src/components/UI/Buttons/Button/Button.tsx index 5e9e9d5..5ad7678 100644 --- a/client/src/components/UI/Buttons/Button/Button.tsx +++ b/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; } diff --git a/client/src/interfaces/Bookmark.ts b/client/src/interfaces/Bookmark.ts index bcbf9ab..db10380 100644 --- a/client/src/interfaces/Bookmark.ts +++ b/client/src/interfaces/Bookmark.ts @@ -1,15 +1,11 @@ import { Model } from '.'; -export interface Bookmark extends Model { - name: string; - url: string; - categoryId: number; - icon: string; -} - export interface NewBookmark { name: string; url: string; categoryId: number; icon: string; -} \ No newline at end of file + isPublic: boolean; +} + +export interface Bookmark extends Model, NewBookmark {} diff --git a/client/src/interfaces/Category.ts b/client/src/interfaces/Category.ts index 0f9f8f9..34270d3 100644 --- a/client/src/interfaces/Category.ts +++ b/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; -} \ No newline at end of file diff --git a/client/src/utility/checkVersion.ts b/client/src/utility/checkVersion.ts index d4cdb9a..7a95679 100644 --- a/client/src/utility/checkVersion.ts +++ b/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 { diff --git a/client/src/utility/templateObjects/appTemplate.ts b/client/src/utility/templateObjects/appTemplate.ts index 8dad83d..da500ab 100644 --- a/client/src/utility/templateObjects/appTemplate.ts +++ b/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(), }; diff --git a/client/src/utility/templateObjects/bookmarkTemplate.ts b/client/src/utility/templateObjects/bookmarkTemplate.ts new file mode 100644 index 0000000..d2b1749 --- /dev/null +++ b/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(), +}; diff --git a/client/src/utility/templateObjects/categoryTemplate.ts b/client/src/utility/templateObjects/categoryTemplate.ts new file mode 100644 index 0000000..9907f94 --- /dev/null +++ b/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(), +}; diff --git a/client/src/utility/templateObjects/index.ts b/client/src/utility/templateObjects/index.ts index 228bed4..bad6ac5 100644 --- a/client/src/utility/templateObjects/index.ts +++ b/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';