Delete and update Bookmarks

This commit is contained in:
unknown 2021-05-28 13:41:27 +02:00
parent 88c8eee982
commit 519b6d0746
7 changed files with 189 additions and 35 deletions

View file

@ -90,12 +90,6 @@ const Apps = (props: ComponentProps): JSX.Element => {
icon='mdiPencil'
handler={toggleEdit}
/>
{isInEdit && <Fragment>
<ActionButton
name='Pin All'
icon='mdiPin'
/>
</Fragment>}
</div>
<div className={classes.Apps}>

View file

@ -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<HTMLFormElement>): 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 = <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}
@ -142,6 +185,7 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
id='categoryId'
required
onChange={(e) => selectChangeHandler(e)}
value={formData.categoryId}
>
<option value={-1}>Select category</option>
{props.categories.map((category: Category): JSX.Element => {
@ -159,10 +203,7 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
</Fragment>
)
}
{!props.category
? <Button>Add new category</Button>
: <Button>Update category</Button>
}
{button}
</ModalForm>
)
}
@ -178,6 +219,7 @@ const dispatchMap = {
addCategory,
addBookmark,
updateCategory,
updateBookmark,
createNotification
}

View file

@ -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 => {
</div>
<div
className={classes.TableAction}
onClick={() => props.updateCategoryHandler(category)}
onClick={() => props.updateHandler(category)}
// onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
tabIndex={0}>
<Icon icon='mdiPencil' />
@ -99,14 +108,14 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
<td className={classes.TableActions}>
<div
className={classes.TableAction}
// onClick={() => deleteAppHandler(app)}
onClick={() => deleteBookmarkHandler(bookmark.bookmark)}
// onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)}
tabIndex={0}>
<Icon icon='mdiDelete' />
</div>
<div
className={classes.TableAction}
// onClick={() => props.updateAppHandler(app)}
onClick={() => props.updateHandler(bookmark.bookmark)}
// onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
tabIndex={0}>
<Icon icon='mdiPencil' />
@ -120,4 +129,4 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
}
}
export default connect(null, { pinCategory, deleteCategory })(BookmarkTable);
export default connect(null, { pinCategory, deleteCategory, deleteBookmark })(BookmarkTable);

View file

@ -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<Bookmark>({
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 (
<Container>
<Modal isOpen={modalIsOpen} setIsOpen={toggleModal}>
{!isInUpdate
? <BookmarkForm modalHandler={toggleModal} contentType={formContentType} />
: <BookmarkForm modalHandler={toggleModal} contentType={formContentType} category={categoryInUpdate} />
: formContentType === ContentType.category
? <BookmarkForm modalHandler={toggleModal} contentType={formContentType} category={categoryInUpdate} />
: <BookmarkForm modalHandler={toggleModal} contentType={formContentType} bookmark={bookmarkInUpdate} />
}
</Modal>
@ -126,7 +140,7 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
: (<BookmarkTable
contentType={tableContentType}
categories={props.categories}
updateCategoryHandler={goToUpdateMode}
updateHandler={goToUpdateMode}
/>)
)
}

View file

@ -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<any> |
AddCategoryAction |
AddBookmarkAction |
PinCategoryAction |
DeleteCategoryAction |
UpdateCategoryAction |
// Bookmarks
AddBookmarkAction |
DeleteBookmarkAction |
UpdateBookmarkAction |
// Notifications
CreateNotificationAction |
ClearNotificationAction;

View file

@ -177,3 +177,67 @@ export const updateCategory = (id: number, formData: NewCategory) => async (disp
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<ApiResponse<{}>>(`/api/bookmarks/${bookmarkId}`);
dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification,
payload: {
title: 'Success',
message: 'Bookmark deleted'
}
})
dispatch<DeleteBookmarkAction>({
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<ApiResponse<Bookmark>>(`/api/bookmarks/${bookmarkId}`, formData);
dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification,
payload: {
title: 'Success',
message: `Bookmark ${formData.name} updated`
}
})
dispatch<UpdateBookmarkAction>({
type: ActionTypes.updateBookmark,
payload: res.data.data
})
} catch (err) {
console.log(err);
}
}

View file

@ -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;
}
}