diff --git a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx
index 388b4d8..4387afc 100644
--- a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx
+++ b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx
@@ -1,5 +1,8 @@
import { ContentType } from '../Bookmarks';
import classes from './BookmarkTable.module.css';
+import { connect } from 'react-redux';
+import { pinCategory, deleteCategory } from '../../../store/actions';
+import { KeyboardEvent } from 'react';
import Table from '../../UI/Table/Table';
import { Bookmark, Category } from '../../../interfaces';
@@ -8,9 +11,25 @@ import Icon from '../../UI/Icons/Icon/Icon';
interface ComponentProps {
contentType: ContentType;
categories: Category[];
+ pinCategory: (category: Category) => void;
+ deleteCategory: (id: number) => void;
}
const BookmarkTable = (props: ComponentProps): JSX.Element => {
+ const deleteCategoryHandler = (category: Category): void => {
+ const proceed = window.confirm(`Are you sure you want to delete ${category.name}? It will delete ALL assigned bookmarks`);
+
+ if (proceed) {
+ props.deleteCategory(category.id);
+ }
+ }
+
+ const keyboardActionHandler = (e: KeyboardEvent, category: Category, handler: Function) => {
+ if (e.key === 'Enter') {
+ handler(category);
+ }
+ }
+
if (props.contentType === ContentType.category) {
return (
{
deleteAppHandler(app)}
- // onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)}
+ onClick={() => deleteCategoryHandler(category)}
+ onKeyDown={(e) => keyboardActionHandler(e, category, deleteCategoryHandler)}
tabIndex={0}>
@@ -38,8 +57,8 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
props.pinApp(app)}
- // onKeyDown={(e) => keyboardActionHandler(e, app, props.pinApp)}
+ onClick={() => props.pinCategory(category)}
+ onKeyDown={(e) => keyboardActionHandler(e, category, props.pinCategory)}
tabIndex={0}>
{category.isPinned
?
@@ -100,4 +119,4 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
}
}
-export default BookmarkTable;
\ No newline at end of file
+export default connect(null, { pinCategory, deleteCategory })(BookmarkTable);
\ No newline at end of file
diff --git a/client/src/components/Bookmarks/Bookmarks.module.css b/client/src/components/Bookmarks/Bookmarks.module.css
index 09810e1..4c17197 100644
--- a/client/src/components/Bookmarks/Bookmarks.module.css
+++ b/client/src/components/Bookmarks/Bookmarks.module.css
@@ -1,4 +1,13 @@
.ActionsContainer {
display: flex;
align-items: center;
+}
+
+.BookmarksMessage {
+ color: var(--color-primary);
+}
+
+.BookmarksMessage a {
+ color: var(--color-accent);
+ font-weight: 600;
}
\ No newline at end of file
diff --git a/client/src/components/Bookmarks/Bookmarks.tsx b/client/src/components/Bookmarks/Bookmarks.tsx
index ba14b5e..6e39cc3 100644
--- a/client/src/components/Bookmarks/Bookmarks.tsx
+++ b/client/src/components/Bookmarks/Bookmarks.tsx
@@ -99,7 +99,9 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
{props.loading
?
: (!isInEdit
- ?
+ ? props.categories.length > 0
+ ?
+ : You don't have any bookmarks. You can a new one from /bookmarks menu
: )
}
diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts
index 153e3fa..db5f344 100644
--- a/client/src/store/actions/actionTypes.ts
+++ b/client/src/store/actions/actionTypes.ts
@@ -11,6 +11,8 @@ import {
GetCategoriesAction,
AddCategoryAction,
AddBookmarkAction,
+ PinCategoryAction,
+ DeleteCategoryAction,
// Notifications
CreateNotificationAction,
ClearNotificationAction
@@ -34,6 +36,8 @@ export enum ActionTypes {
getCategoriesError = 'GET_CATEGORIES_ERROR',
addCategory = 'ADD_CATEGORY',
addBookmark = 'ADD_BOOKMARK',
+ pinCategory = 'PIN_CATEGORY',
+ deleteCategory = 'DELETE_CATEGORY',
// Notifications
createNotification = 'CREATE_NOTIFICATION',
clearNotification = 'CLEAR_NOTIFICATION'
@@ -52,6 +56,8 @@ export type Action =
GetCategoriesAction |
AddCategoryAction |
AddBookmarkAction |
+ PinCategoryAction |
+ DeleteCategoryAction |
// 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 7c121dc..933be1b 100644
--- a/client/src/store/actions/bookmark.ts
+++ b/client/src/store/actions/bookmark.ts
@@ -4,6 +4,9 @@ import { ActionTypes } from './actionTypes';
import { Category, ApiResponse, NewCategory, Bookmark, NewBookmark } from '../../interfaces';
import { CreateNotificationAction } from './notification';
+/**
+ * GET CATEGORIES
+ */
export interface GetCategoriesAction {
type: ActionTypes.getCategories | ActionTypes.getCategoriesSuccess | ActionTypes.getCategoriesError;
payload: T;
@@ -27,6 +30,9 @@ export const getCategories = () => async (dispatch: Dispatch) => {
}
}
+/**
+ * ADD CATEGORY
+ */
export interface AddCategoryAction {
type: ActionTypes.addCategory,
payload: Category
@@ -53,6 +59,9 @@ export const addCategory = (formData: NewCategory) => async (dispatch: Dispatch)
}
}
+/**
+ * ADD BOOKMARK
+ */
export interface AddBookmarkAction {
type: ActionTypes.addBookmark,
payload: Bookmark
@@ -77,4 +86,65 @@ export const addBookmark = (formData: NewBookmark) => async (dispatch: Dispatch)
} catch (err) {
console.log(err);
}
+}
+
+/**
+ * PIN CATEGORY
+ */
+export interface PinCategoryAction {
+ type: ActionTypes.pinCategory,
+ payload: Category
+}
+
+export const pinCategory = (category: Category) => async (dispatch: Dispatch) => {
+ try {
+ const { id, isPinned, name } = category;
+ const res = await axios.put>(`/api/categories/${id}`, { isPinned: !isPinned });
+
+ const status = isPinned ? 'unpinned from Homescreen' : 'pinned to Homescreen';
+
+ dispatch({
+ type: ActionTypes.createNotification,
+ payload: {
+ title: 'Success',
+ message: `Category ${name} ${status}`
+ }
+ })
+
+ dispatch({
+ type: ActionTypes.pinCategory,
+ payload: res.data.data
+ })
+ } catch (err) {
+ console.log(err);
+ }
+}
+
+/**
+ * DELETE CATEGORY
+ */
+export interface DeleteCategoryAction {
+ type: ActionTypes.deleteCategory,
+ payload: number
+}
+
+export const deleteCategory = (id: number) => async (dispatch: Dispatch) => {
+ try {
+ const res = await axios.delete>(`/api/categories/${id}`);
+
+ dispatch({
+ type: ActionTypes.createNotification,
+ payload: {
+ title: 'Success',
+ message: `Category deleted`
+ }
+ })
+
+ dispatch({
+ type: ActionTypes.deleteCategory,
+ payload: id
+ })
+ } 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 6c4990f..997d880 100644
--- a/client/src/store/reducers/bookmark.ts
+++ b/client/src/store/reducers/bookmark.ts
@@ -55,12 +55,37 @@ const addBookmark = (state: State, action: Action): State => {
}
}
+const pinCategory = (state: State, action: Action): State => {
+ const tmpCategories = [...state.categories];
+ const changedCategory = tmpCategories.find((category: Category) => category.id === action.payload.id);
+
+ if (changedCategory) {
+ changedCategory.isPinned = action.payload.isPinned;
+ }
+
+ return {
+ ...state,
+ categories: tmpCategories
+ }
+}
+
+const deleteCategory = (state: State, action: Action): State => {
+ const tmpCategories = [...state.categories].filter((category: Category) => category.id !== action.payload);
+
+ return {
+ ...state,
+ categories: tmpCategories
+ }
+}
+
const bookmarkReducer = (state = initialState, action: Action) => {
switch (action.type) {
case ActionTypes.getCategories: return getCategories(state, action);
case ActionTypes.getCategoriesSuccess: return getCategoriesSuccess(state, action);
case ActionTypes.addCategory: return addCategory(state, action);
case ActionTypes.addBookmark: return addBookmark(state, action);
+ case ActionTypes.pinCategory: return pinCategory(state, action);
+ case ActionTypes.deleteCategory: return deleteCategory(state, action);
default: return state;
}
}
diff --git a/controllers/category.js b/controllers/category.js
index 5ab26df..60e7305 100644
--- a/controllers/category.js
+++ b/controllers/category.js
@@ -79,6 +79,24 @@ exports.updateCategory = asyncWrapper(async (req, res, next) => {
// @route DELETE /api/categories/:id
// @access Public
exports.deleteCategory = asyncWrapper(async (req, res, next) => {
+ const category = await Category.findOne({
+ where: { id: req.params.id },
+ include: [{
+ model: Bookmark,
+ as: 'bookmarks'
+ }]
+ });
+
+ if (!category) {
+ return next(new ErrorResponse(`Category with id of ${req.params.id} was not found`, 404))
+ }
+
+ category.bookmarks.forEach(async (bookmark) => {
+ await Bookmark.destroy({
+ where: { id: bookmark.id }
+ })
+ })
+
await Category.destroy({
where: { id: req.params.id }
})
diff --git a/models/associateModels.js b/models/associateModels.js
index 4ca1fab..2457092 100644
--- a/models/associateModels.js
+++ b/models/associateModels.js
@@ -3,11 +3,11 @@ const Bookmark = require('./Bookmark');
const associateModels = () => {
// Category <> Bookmark
- Bookmark.belongsTo(Category, { foreignKey: 'categoryId' });
Category.hasMany(Bookmark, {
as: 'bookmarks',
foreignKey: 'categoryId'
});
+ Bookmark.belongsTo(Category, { foreignKey: 'categoryId' });
}
module.exports = associateModels;
\ No newline at end of file
|