diff --git a/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx b/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx
index 6757f54..6d0b020 100644
--- a/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx
+++ b/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx
@@ -14,7 +14,7 @@ const BookmarkCard = (props: ComponentProps): JSX.Element => {
+ key={`bookmark-${bookmark.id}`}>
{bookmark.name}
))}
diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx
index d299070..728baaa 100644
--- a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx
+++ b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx
@@ -5,12 +5,14 @@ import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
import { Category, GlobalState, NewBookmark, NewCategory } from '../../../interfaces';
import { FormContentType } from '../Bookmarks';
-import { getCategories } from '../../../store/actions';
+import { getCategories, addCategory, addBookmark } from '../../../store/actions';
interface ComponentProps {
modalHandler: () => void;
contentType: FormContentType;
categories: Category[];
+ addCategory: (formData: NewCategory) => void;
+ addBookmark: (formData: NewBookmark) => void;
}
const BookmarkForm = (props: ComponentProps): JSX.Element => {
@@ -27,8 +29,21 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
const formSubmitHandler = (e: SyntheticEvent): void => {
e.preventDefault();
- if (formData.categoryId === -1) {
- alert('select category');
+ if (props.contentType === FormContentType.category) {
+ props.addCategory(categoryName);
+ setCategoryName({ name: '' });
+ } else if (props.contentType === FormContentType.bookmark) {
+ if (formData.categoryId === -1) {
+ alert('select category');
+ return;
+ }
+
+ props.addBookmark(formData);
+ setFormData({
+ name: '',
+ url: '',
+ categoryId: formData.categoryId
+ })
}
}
@@ -129,4 +144,4 @@ const mapStateToProps = (state: GlobalState) => {
}
}
-export default connect(mapStateToProps, { getCategories })(BookmarkForm);
\ No newline at end of file
+export default connect(mapStateToProps, { getCategories, addCategory, addBookmark })(BookmarkForm);
\ No newline at end of file
diff --git a/client/src/components/UI/Forms/InputGroup/InputGroup.module.css b/client/src/components/UI/Forms/InputGroup/InputGroup.module.css
index d8b9fca..bb4cc3c 100644
--- a/client/src/components/UI/Forms/InputGroup/InputGroup.module.css
+++ b/client/src/components/UI/Forms/InputGroup/InputGroup.module.css
@@ -8,7 +8,8 @@
display: block;
}
-.InputGroup input {
+.InputGroup input,
+.InputGroup select {
margin: 8px 0;
width: 100%;
border: none;
diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts
index f6ee1f5..b2e46ad 100644
--- a/client/src/store/actions/actionTypes.ts
+++ b/client/src/store/actions/actionTypes.ts
@@ -1,15 +1,22 @@
import {
- GetAppsAction,
+ // Theme
SetThemeAction,
+ // Apps
+ GetAppsAction,
PinAppAction,
AddAppAction,
DeleteAppAction,
UpdateAppAction,
- GetCategoriesAction
+ // Categories
+ GetCategoriesAction,
+ AddCategoryAction,
+ AddBookmarkAction
} from './';
export enum ActionTypes {
+ // Theme
setTheme = 'SET_THEME',
+ // Apps
getApps = 'GET_APPS',
getAppsSuccess = 'GET_APPS_SUCCESS',
getAppsError = 'GET_APPS_ERROR',
@@ -18,9 +25,24 @@ export enum ActionTypes {
addAppSuccess = 'ADD_APP_SUCCESS',
deleteApp = 'DELETE_APP',
updateApp = 'UPDATE_APP',
+ // Categories
getCategories = 'GET_CATEGORIES',
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
- getCategoriesError = 'GET_CATEGORIES_ERROR'
+ getCategoriesError = 'GET_CATEGORIES_ERROR',
+ addCategory = 'ADD_CATEGORY',
+ addBookmark = 'ADD_BOOKMARK'
}
-export type Action = GetAppsAction | SetThemeAction | PinAppAction | AddAppAction | DeleteAppAction | UpdateAppAction | GetCategoriesAction;
\ No newline at end of file
+export type Action =
+ // Theme
+ SetThemeAction |
+ // Apps
+ GetAppsAction |
+ PinAppAction |
+ AddAppAction |
+ DeleteAppAction |
+ UpdateAppAction |
+ // Categories
+ GetCategoriesAction |
+ AddCategoryAction |
+ AddBookmarkAction;
\ No newline at end of file
diff --git a/client/src/store/actions/bookmark.ts b/client/src/store/actions/bookmark.ts
index ea074cc..b1bd6f1 100644
--- a/client/src/store/actions/bookmark.ts
+++ b/client/src/store/actions/bookmark.ts
@@ -1,7 +1,7 @@
import axios from 'axios';
import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes';
-import { Category, ApiResponse } from '../../interfaces';
+import { Category, ApiResponse, NewCategory, Bookmark, NewBookmark } from '../../interfaces';
export interface GetCategoriesAction {
type: ActionTypes.getCategories | ActionTypes.getCategoriesSuccess | ActionTypes.getCategoriesError;
@@ -24,4 +24,40 @@ export const getCategories = () => async (dispatch: Dispatch) => {
} catch (err) {
console.log(err);
}
+}
+
+export interface AddCategoryAction {
+ type: ActionTypes.addCategory,
+ payload: Category
+}
+
+export const addCategory = (formData: NewCategory) => async (dispatch: Dispatch) => {
+ try {
+ const res = await axios.post>('/api/categories', formData);
+
+ dispatch({
+ type: ActionTypes.addCategory,
+ payload: res.data.data
+ })
+ } catch (err) {
+ console.log(err);
+ }
+}
+
+export interface AddBookmarkAction {
+ type: ActionTypes.addBookmark,
+ payload: Bookmark
+}
+
+export const addBookmark = (formData: NewBookmark) => async (dispatch: Dispatch) => {
+ try {
+ const res = await axios.post>('/api/bookmarks', formData);
+
+ dispatch({
+ type: ActionTypes.addBookmark,
+ 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 cb8b169..6c4990f 100644
--- a/client/src/store/reducers/bookmark.ts
+++ b/client/src/store/reducers/bookmark.ts
@@ -27,12 +27,40 @@ const getCategoriesSuccess = (state: State, action: Action): State => {
loading: false,
categories: action.payload
}
-}
+}
+
+const addCategory = (state: State, action: Action): State => {
+ const tmpCategories = [...state.categories, {
+ ...action.payload,
+ bookmarks: []
+ }];
+
+ return {
+ ...state,
+ categories: tmpCategories
+ }
+}
+
+const addBookmark = (state: State, action: Action): State => {
+ const tmpCategories = [...state.categories];
+ const tmpCategory = tmpCategories.find((category: Category) => category.id === action.payload.categoryId);
+
+ if (tmpCategory) {
+ tmpCategory.bookmarks.push(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);
default: return state;
}
}
diff --git a/controllers/category.js b/controllers/category.js
index 0b47120..5ab26df 100644
--- a/controllers/category.js
+++ b/controllers/category.js
@@ -23,7 +23,8 @@ exports.getCategories = asyncWrapper(async (req, res, next) => {
include: [{
model: Bookmark,
as: 'bookmarks'
- }]
+ }],
+ order: [['name', 'ASC']]
});
res.status(200).json({