diff --git a/client/src/components/Apps/Apps.tsx b/client/src/components/Apps/Apps.tsx index 9eaf97f..a062c56 100644 --- a/client/src/components/Apps/Apps.tsx +++ b/client/src/components/Apps/Apps.tsx @@ -90,12 +90,6 @@ const Apps = (props: ComponentProps): JSX.Element => { icon='mdiPencil' handler={toggleEdit} /> - {isInEdit && - - }
diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx index caccbcc..4b39b14 100644 --- a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx +++ b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx @@ -3,9 +3,9 @@ import { connect } from 'react-redux'; import ModalForm from '../../UI/Forms/ModalForm/ModalForm'; import InputGroup from '../../UI/Forms/InputGroup/InputGroup'; -import { Category, GlobalState, NewBookmark, NewCategory, NewNotification } from '../../../interfaces'; +import { Bookmark, Category, GlobalState, NewBookmark, NewCategory, NewNotification } from '../../../interfaces'; import { ContentType } from '../Bookmarks'; -import { getCategories, addCategory, addBookmark, updateCategory, createNotification } from '../../../store/actions'; +import { getCategories, addCategory, addBookmark, updateCategory, updateBookmark, createNotification } from '../../../store/actions'; import Button from '../../UI/Buttons/Button/Button'; interface ComponentProps { @@ -13,9 +13,11 @@ interface ComponentProps { contentType: ContentType; categories: Category[]; category?: Category; + bookmark?: Bookmark; addCategory: (formData: NewCategory) => void; addBookmark: (formData: NewBookmark) => void; updateCategory: (id: number, formData: NewCategory) => void; + updateBookmark: (id: number, formData: NewBookmark) => void; createNotification: (notification: NewNotification) => void; } @@ -38,15 +40,33 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { } }, [props.category]) + useEffect(() => { + if (props.bookmark) { + setFormData({ + name: props.bookmark.name, + url: props.bookmark.url, + categoryId: props.bookmark.categoryId + }) + } else { + setFormData({ + name: '', + url: '', + categoryId: -1 + }) + } + }, [props.bookmark]) + const formSubmitHandler = (e: SyntheticEvent): void => { e.preventDefault(); - if (!props.category) { + if (!props.category && !props.bookmark) { // Add new if (props.contentType === ContentType.category) { + // Add category props.addCategory(categoryName); setCategoryName({ name: '' }); } else if (props.contentType === ContentType.bookmark) { + // Add bookmark if (formData.categoryId === -1) { props.createNotification({ title: 'Error', @@ -64,9 +84,18 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { } } else { // Update - if (props.contentType === ContentType.category) { + if (props.contentType === ContentType.category && props.category) { + // Update category props.updateCategory(props.category.id, categoryName); setCategoryName({ name: '' }); + } else if (props.contentType === ContentType.bookmark && props.bookmark) { + // Update bookmark + props.updateBookmark(props.bookmark.id, formData); + setFormData({ + name: '', + url: '', + categoryId: -1 + }) } props.modalHandler(); @@ -87,6 +116,20 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { }) } + 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 ( { id='categoryId' required onChange={(e) => selectChangeHandler(e)} + value={formData.categoryId} > {props.categories.map((category: Category): JSX.Element => { @@ -159,10 +203,7 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { ) } - {!props.category - ? - : - } + {button} ) } @@ -178,6 +219,7 @@ const dispatchMap = { addCategory, addBookmark, updateCategory, + updateBookmark, createNotification } diff --git a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx index 29ee7d1..b674bea 100644 --- a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx +++ b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx @@ -1,7 +1,7 @@ import { ContentType } from '../Bookmarks'; import classes from './BookmarkTable.module.css'; import { connect } from 'react-redux'; -import { pinCategory, deleteCategory } from '../../../store/actions'; +import { pinCategory, deleteCategory, deleteBookmark } from '../../../store/actions'; import { KeyboardEvent } from 'react'; import Table from '../../UI/Table/Table'; @@ -13,7 +13,8 @@ interface ComponentProps { categories: Category[]; pinCategory: (category: Category) => void; deleteCategory: (id: number) => void; - updateCategoryHandler: (category: Category) => void; + updateHandler: (data: Category | Bookmark) => void; + deleteBookmark: (bookmarkId: number, categoryId: number) => void; } const BookmarkTable = (props: ComponentProps): JSX.Element => { @@ -25,6 +26,14 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => { } } + const deleteBookmarkHandler = (bookmark: Bookmark): void => { + const proceed = window.confirm(`Are you sure you want to delete ${bookmark.name}?`); + + if (proceed) { + props.deleteBookmark(bookmark.id, bookmark.categoryId); + } + } + const keyboardActionHandler = (e: KeyboardEvent, category: Category, handler: Function) => { if (e.key === 'Enter') { handler(category); @@ -51,7 +60,7 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
props.updateCategoryHandler(category)} + onClick={() => props.updateHandler(category)} // onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)} tabIndex={0}> @@ -99,14 +108,14 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
deleteAppHandler(app)} + onClick={() => deleteBookmarkHandler(bookmark.bookmark)} // onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)} tabIndex={0}>
props.updateAppHandler(app)} + onClick={() => props.updateHandler(bookmark.bookmark)} // onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)} tabIndex={0}> @@ -120,4 +129,4 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => { } } -export default connect(null, { pinCategory, deleteCategory })(BookmarkTable); \ No newline at end of file +export default connect(null, { pinCategory, deleteCategory, deleteBookmark })(BookmarkTable); \ No newline at end of file diff --git a/client/src/components/Bookmarks/Bookmarks.tsx b/client/src/components/Bookmarks/Bookmarks.tsx index 9346262..b5f7b9b 100644 --- a/client/src/components/Bookmarks/Bookmarks.tsx +++ b/client/src/components/Bookmarks/Bookmarks.tsx @@ -10,7 +10,7 @@ import Headline from '../UI/Headlines/Headline/Headline'; import ActionButton from '../UI/Buttons/ActionButton/ActionButton'; import BookmarkGrid from './BookmarkGrid/BookmarkGrid'; -import { Category, GlobalState } from '../../interfaces'; +import { Category, GlobalState, Bookmark } from '../../interfaces'; import Spinner from '../UI/Spinner/Spinner'; import Modal from '../UI/Modal/Modal'; import BookmarkForm from './BookmarkForm/BookmarkForm'; @@ -41,6 +41,14 @@ const Bookmarks = (props: ComponentProps): JSX.Element => { createdAt: new Date(), updatedAt: new Date() }) + const [bookmarkInUpdate, setBookmarkInUpdate] = useState({ + name: '', + url: '', + categoryId: -1, + id: -1, + createdAt: new Date(), + updatedAt: new Date() + }) useEffect(() => { if (props.categories.length === 0) { @@ -58,10 +66,6 @@ const Bookmarks = (props: ComponentProps): JSX.Element => { toggleModal(); } - const toggleEdit = (): void => { - setIsInEdit(!isInEdit); - } - const editActionHandler = (contentType: ContentType) => { // We're in the edit mode and the same button was clicked - go back to list if (isInEdit && contentType === tableContentType) { @@ -72,20 +76,30 @@ const Bookmarks = (props: ComponentProps): JSX.Element => { } } - const goToUpdateMode = (category: Category): void => { - setIsInUpdate(true); - setCategoryInUpdate(category); - toggleModal(); + const instanceOfCategory = (object: any): object is Category => { + return 'bookmarks' in object; } - let modalForm: JSX.Element; + const goToUpdateMode = (data: Category | Bookmark): void => { + setIsInUpdate(true); + if (instanceOfCategory(data)) { + setFormContentType(ContentType.category); + setCategoryInUpdate(data); + } else { + setFormContentType(ContentType.bookmark); + setBookmarkInUpdate(data); + } + toggleModal(); + } return ( {!isInUpdate ? - : + : formContentType === ContentType.category + ? + : } @@ -126,7 +140,7 @@ const Bookmarks = (props: ComponentProps): JSX.Element => { : () ) } diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts index 8b8b523..4ff088c 100644 --- a/client/src/store/actions/actionTypes.ts +++ b/client/src/store/actions/actionTypes.ts @@ -10,10 +10,13 @@ import { // Categories GetCategoriesAction, AddCategoryAction, - AddBookmarkAction, PinCategoryAction, DeleteCategoryAction, UpdateCategoryAction, + // Bookmarks + AddBookmarkAction, + DeleteBookmarkAction, + UpdateBookmarkAction, // Notifications CreateNotificationAction, ClearNotificationAction @@ -36,10 +39,13 @@ export enum ActionTypes { getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS', getCategoriesError = 'GET_CATEGORIES_ERROR', addCategory = 'ADD_CATEGORY', - addBookmark = 'ADD_BOOKMARK', pinCategory = 'PIN_CATEGORY', deleteCategory = 'DELETE_CATEGORY', updateCategory = 'UPDATE_CATEGORY', + // Bookmarks + addBookmark = 'ADD_BOOKMARK', + deleteBookmark = 'DELETE_BOOKMARK', + updateBookmark = 'UPDATE_BOOKMARK', // Notifications createNotification = 'CREATE_NOTIFICATION', clearNotification = 'CLEAR_NOTIFICATION' @@ -57,10 +63,13 @@ export type Action = // Categories GetCategoriesAction | AddCategoryAction | - AddBookmarkAction | PinCategoryAction | DeleteCategoryAction | UpdateCategoryAction | + // Bookmarks + AddBookmarkAction | + DeleteBookmarkAction | + UpdateBookmarkAction | // Notifications CreateNotificationAction | ClearNotificationAction; \ No newline at end of file diff --git a/client/src/store/actions/bookmark.ts b/client/src/store/actions/bookmark.ts index 279d740..013846b 100644 --- a/client/src/store/actions/bookmark.ts +++ b/client/src/store/actions/bookmark.ts @@ -176,4 +176,68 @@ export const updateCategory = (id: number, formData: NewCategory) => async (disp } catch (err) { console.log(err); } +} + +/** + * DELETE BOOKMARK + */ +export interface DeleteBookmarkAction { + type: ActionTypes.deleteBookmark, + payload: { + bookmarkId: number, + categoryId: number + } +} + +export const deleteBookmark = (bookmarkId: number, categoryId: number) => async (dispatch: Dispatch) => { + try { + const res = await axios.delete>(`/api/bookmarks/${bookmarkId}`); + + dispatch({ + type: ActionTypes.createNotification, + payload: { + title: 'Success', + message: 'Bookmark deleted' + } + }) + + dispatch({ + type: ActionTypes.deleteBookmark, + payload: { + bookmarkId, + categoryId + } + }) + } catch (err) { + console.log(err); + } +} + +/** + * UPDATE BOOKMARK + */ +export interface UpdateBookmarkAction { + type: ActionTypes.updateBookmark, + payload: Bookmark +} + +export const updateBookmark = (bookmarkId: number, formData: NewBookmark) => async (dispatch: Dispatch) => { + try { + const res = await axios.put>(`/api/bookmarks/${bookmarkId}`, formData); + + dispatch({ + type: ActionTypes.createNotification, + payload: { + title: 'Success', + message: `Bookmark ${formData.name} updated` + } + }) + + dispatch({ + type: ActionTypes.updateBookmark, + payload: res.data.data + }) + } catch (err) { + console.log(err); + } } \ No newline at end of file diff --git a/client/src/store/reducers/bookmark.ts b/client/src/store/reducers/bookmark.ts index 4a75491..d650a1a 100644 --- a/client/src/store/reducers/bookmark.ts +++ b/client/src/store/reducers/bookmark.ts @@ -92,6 +92,26 @@ const updateCategory = (state: State, action: Action): State => { } } +const deleteBookmark = (state: State, action: Action): State => { + const tmpCategories = [...state.categories]; + const categoryInUpdate = tmpCategories.find((category: Category) => category.id === action.payload.categoryId); + + if (categoryInUpdate) { + categoryInUpdate.bookmarks = categoryInUpdate.bookmarks.filter((bookmark: Bookmark) => bookmark.id !== action.payload.bookmarkId); + } + + return { + ...state, + categories: tmpCategories + } +} + +const updateBookmark = (state: State, action: Action): State => { + return { + ...state + } +} + const bookmarkReducer = (state = initialState, action: Action) => { switch (action.type) { case ActionTypes.getCategories: return getCategories(state, action); @@ -101,6 +121,8 @@ const bookmarkReducer = (state = initialState, action: Action) => { case ActionTypes.pinCategory: return pinCategory(state, action); case ActionTypes.deleteCategory: return deleteCategory(state, action); case ActionTypes.updateCategory: return updateCategory(state, action); + case ActionTypes.deleteBookmark: return deleteBookmark(state, action); + case ActionTypes.updateBookmark: return updateBookmark(state, action); default: return state; } }