Delete and update Bookmarks
This commit is contained in:
parent
88c8eee982
commit
519b6d0746
7 changed files with 189 additions and 35 deletions
|
@ -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}>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -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}
|
||||
/>)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue