Adding Categories and Bookmarks using form

This commit is contained in:
unknown 2021-05-24 12:35:54 +02:00
parent 38edb76929
commit c145888aec
7 changed files with 116 additions and 13 deletions

View File

@ -14,7 +14,7 @@ const BookmarkCard = (props: ComponentProps): JSX.Element => {
<a <a
href={`http://${bookmark.url}`} href={`http://${bookmark.url}`}
target='blank' target='blank'
key={bookmark.id}> key={`bookmark-${bookmark.id}`}>
{bookmark.name} {bookmark.name}
</a> </a>
))} ))}

View File

@ -5,12 +5,14 @@ import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
import InputGroup from '../../UI/Forms/InputGroup/InputGroup'; import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
import { Category, GlobalState, NewBookmark, NewCategory } from '../../../interfaces'; import { Category, GlobalState, NewBookmark, NewCategory } from '../../../interfaces';
import { FormContentType } from '../Bookmarks'; import { FormContentType } from '../Bookmarks';
import { getCategories } from '../../../store/actions'; import { getCategories, addCategory, addBookmark } from '../../../store/actions';
interface ComponentProps { interface ComponentProps {
modalHandler: () => void; modalHandler: () => void;
contentType: FormContentType; contentType: FormContentType;
categories: Category[]; categories: Category[];
addCategory: (formData: NewCategory) => void;
addBookmark: (formData: NewBookmark) => void;
} }
const BookmarkForm = (props: ComponentProps): JSX.Element => { const BookmarkForm = (props: ComponentProps): JSX.Element => {
@ -27,8 +29,21 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => { const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => {
e.preventDefault(); e.preventDefault();
if (formData.categoryId === -1) { if (props.contentType === FormContentType.category) {
alert('select 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); export default connect(mapStateToProps, { getCategories, addCategory, addBookmark })(BookmarkForm);

View File

@ -8,7 +8,8 @@
display: block; display: block;
} }
.InputGroup input { .InputGroup input,
.InputGroup select {
margin: 8px 0; margin: 8px 0;
width: 100%; width: 100%;
border: none; border: none;

View File

@ -1,15 +1,22 @@
import { import {
GetAppsAction, // Theme
SetThemeAction, SetThemeAction,
// Apps
GetAppsAction,
PinAppAction, PinAppAction,
AddAppAction, AddAppAction,
DeleteAppAction, DeleteAppAction,
UpdateAppAction, UpdateAppAction,
GetCategoriesAction // Categories
GetCategoriesAction,
AddCategoryAction,
AddBookmarkAction
} from './'; } from './';
export enum ActionTypes { export enum ActionTypes {
// Theme
setTheme = 'SET_THEME', setTheme = 'SET_THEME',
// Apps
getApps = 'GET_APPS', getApps = 'GET_APPS',
getAppsSuccess = 'GET_APPS_SUCCESS', getAppsSuccess = 'GET_APPS_SUCCESS',
getAppsError = 'GET_APPS_ERROR', getAppsError = 'GET_APPS_ERROR',
@ -18,9 +25,24 @@ export enum ActionTypes {
addAppSuccess = 'ADD_APP_SUCCESS', addAppSuccess = 'ADD_APP_SUCCESS',
deleteApp = 'DELETE_APP', deleteApp = 'DELETE_APP',
updateApp = 'UPDATE_APP', updateApp = 'UPDATE_APP',
// Categories
getCategories = 'GET_CATEGORIES', getCategories = 'GET_CATEGORIES',
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS', getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
getCategoriesError = 'GET_CATEGORIES_ERROR' getCategoriesError = 'GET_CATEGORIES_ERROR',
addCategory = 'ADD_CATEGORY',
addBookmark = 'ADD_BOOKMARK'
} }
export type Action = GetAppsAction<any> | SetThemeAction | PinAppAction | AddAppAction | DeleteAppAction | UpdateAppAction | GetCategoriesAction<any>; export type Action =
// Theme
SetThemeAction |
// Apps
GetAppsAction<any> |
PinAppAction |
AddAppAction |
DeleteAppAction |
UpdateAppAction |
// Categories
GetCategoriesAction<any> |
AddCategoryAction |
AddBookmarkAction;

View File

@ -1,7 +1,7 @@
import axios from 'axios'; import axios from 'axios';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes'; import { ActionTypes } from './actionTypes';
import { Category, ApiResponse } from '../../interfaces'; import { Category, ApiResponse, NewCategory, Bookmark, NewBookmark } from '../../interfaces';
export interface GetCategoriesAction<T> { export interface GetCategoriesAction<T> {
type: ActionTypes.getCategories | ActionTypes.getCategoriesSuccess | ActionTypes.getCategoriesError; type: ActionTypes.getCategories | ActionTypes.getCategoriesSuccess | ActionTypes.getCategoriesError;
@ -25,3 +25,39 @@ export const getCategories = () => async (dispatch: Dispatch) => {
console.log(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<ApiResponse<Category>>('/api/categories', formData);
dispatch<AddCategoryAction>({
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<ApiResponse<Bookmark>>('/api/bookmarks', formData);
dispatch<AddBookmarkAction>({
type: ActionTypes.addBookmark,
payload: res.data.data
})
} catch (err) {
console.log(err);
}
}

View File

@ -29,10 +29,38 @@ const getCategoriesSuccess = (state: State, action: Action): State => {
} }
} }
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) => { const bookmarkReducer = (state = initialState, action: Action) => {
switch (action.type) { switch (action.type) {
case ActionTypes.getCategories: return getCategories(state, action); case ActionTypes.getCategories: return getCategories(state, action);
case ActionTypes.getCategoriesSuccess: return getCategoriesSuccess(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; default: return state;
} }
} }

View File

@ -23,7 +23,8 @@ exports.getCategories = asyncWrapper(async (req, res, next) => {
include: [{ include: [{
model: Bookmark, model: Bookmark,
as: 'bookmarks' as: 'bookmarks'
}] }],
order: [['name', 'ASC']]
}); });
res.status(200).json({ res.status(200).json({