diff --git a/client/src/components/Apps/AppForm/AppForm.tsx b/client/src/components/Apps/AppForm/AppForm.tsx
index dd3c85d..5ccb603 100644
--- a/client/src/components/Apps/AppForm/AppForm.tsx
+++ b/client/src/components/Apps/AppForm/AppForm.tsx
@@ -5,6 +5,7 @@ import { App, NewApp } from '../../../interfaces';
import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
+import Button from '../../UI/Buttons/Button/Button';
interface ComponentProps {
modalHandler: () => void;
@@ -35,6 +36,12 @@ const AppForm = (props: ComponentProps): JSX.Element => {
url: props.app.url,
icon: props.app.icon
})
+ } else {
+ setFormData({
+ name: '',
+ url: '',
+ icon: ''
+ })
}
}, [props.app])
@@ -114,8 +121,8 @@ const AppForm = (props: ComponentProps): JSX.Element => {
{!props.app
- ?
- :
+ ?
+ :
}
)
diff --git a/client/src/components/Apps/AppGrid/AppGrid.module.css b/client/src/components/Apps/AppGrid/AppGrid.module.css
index e8ee751..42729c9 100644
--- a/client/src/components/Apps/AppGrid/AppGrid.module.css
+++ b/client/src/components/Apps/AppGrid/AppGrid.module.css
@@ -21,8 +21,11 @@
}
}
-/* 320px — 480px: Mobile devices.
-481px — 768px: iPads, Tablets.
-769px — 1024px: Small screens, laptops.
-1025px — 1200px: Desktops, large screens.
-1201px and more — Extra large screens, TV. */
\ No newline at end of file
+.GridMessage {
+ color: var(--color-primary);
+}
+
+.GridMessage a {
+ color: var(--color-accent);
+ font-weight: 600;
+}
\ No newline at end of file
diff --git a/client/src/components/Apps/Apps.tsx b/client/src/components/Apps/Apps.tsx
index ae76a0a..9eaf97f 100644
--- a/client/src/components/Apps/Apps.tsx
+++ b/client/src/components/Apps/Apps.tsx
@@ -104,7 +104,7 @@ const Apps = (props: ComponentProps): JSX.Element => {
: (!isInEdit
? props.apps.length > 0
?
- :
You don't have any applications. You can a new one from /application menu
+ : You don't have any applications. You can add a new one from /application menu
: )
}
diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx
index 0d6223e..caccbcc 100644
--- a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx
+++ b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx
@@ -3,16 +3,20 @@ import { connect } from 'react-redux';
import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
-import { Category, GlobalState, NewBookmark, NewCategory } from '../../../interfaces';
+import { Category, GlobalState, NewBookmark, NewCategory, NewNotification } from '../../../interfaces';
import { ContentType } from '../Bookmarks';
-import { getCategories, addCategory, addBookmark } from '../../../store/actions';
+import { getCategories, addCategory, addBookmark, updateCategory, createNotification } from '../../../store/actions';
+import Button from '../../UI/Buttons/Button/Button';
interface ComponentProps {
modalHandler: () => void;
contentType: ContentType;
categories: Category[];
+ category?: Category;
addCategory: (formData: NewCategory) => void;
addBookmark: (formData: NewBookmark) => void;
+ updateCategory: (id: number, formData: NewCategory) => void;
+ createNotification: (notification: NewNotification) => void;
}
const BookmarkForm = (props: ComponentProps): JSX.Element => {
@@ -26,24 +30,46 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
categoryId: -1
})
+ useEffect(() => {
+ if (props.category) {
+ setCategoryName({ name: props.category.name });
+ } else {
+ setCategoryName({ name: '' });
+ }
+ }, [props.category])
+
const formSubmitHandler = (e: SyntheticEvent): void => {
e.preventDefault();
-
- if (props.contentType === ContentType.category) {
- props.addCategory(categoryName);
- setCategoryName({ name: '' });
- } else if (props.contentType === ContentType.bookmark) {
- if (formData.categoryId === -1) {
- alert('select category');
- return;
+
+ if (!props.category) {
+ // Add new
+ if (props.contentType === ContentType.category) {
+ props.addCategory(categoryName);
+ setCategoryName({ name: '' });
+ } else if (props.contentType === ContentType.bookmark) {
+ if (formData.categoryId === -1) {
+ props.createNotification({
+ title: 'Error',
+ message: 'Please select category'
+ })
+ return;
+ }
+
+ props.addBookmark(formData);
+ setFormData({
+ name: '',
+ url: '',
+ categoryId: formData.categoryId
+ })
+ }
+ } else {
+ // Update
+ if (props.contentType === ContentType.category) {
+ props.updateCategory(props.category.id, categoryName);
+ setCategoryName({ name: '' });
}
- props.addBookmark(formData);
- setFormData({
- name: '',
- url: '',
- categoryId: formData.categoryId
- })
+ props.modalHandler();
}
}
@@ -133,7 +159,10 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
)
}
-
+ {!props.category
+ ?
+ :
+ }
)
}
@@ -144,4 +173,12 @@ const mapStateToProps = (state: GlobalState) => {
}
}
-export default connect(mapStateToProps, { getCategories, addCategory, addBookmark })(BookmarkForm);
\ No newline at end of file
+const dispatchMap = {
+ getCategories,
+ addCategory,
+ addBookmark,
+ updateCategory,
+ createNotification
+}
+
+export default connect(mapStateToProps, dispatchMap)(BookmarkForm);
\ No newline at end of file
diff --git a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx
index 4387afc..29ee7d1 100644
--- a/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx
+++ b/client/src/components/Bookmarks/BookmarkTable/BookmarkTable.tsx
@@ -13,6 +13,7 @@ interface ComponentProps {
categories: Category[];
pinCategory: (category: Category) => void;
deleteCategory: (id: number) => void;
+ updateCategoryHandler: (category: Category) => void;
}
const BookmarkTable = (props: ComponentProps): JSX.Element => {
@@ -50,7 +51,7 @@ const BookmarkTable = (props: ComponentProps): JSX.Element => {
props.updateAppHandler(app)}
+ onClick={() => props.updateCategoryHandler(category)}
// onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
tabIndex={0}>
diff --git a/client/src/components/Bookmarks/Bookmarks.tsx b/client/src/components/Bookmarks/Bookmarks.tsx
index 6e39cc3..9346262 100644
--- a/client/src/components/Bookmarks/Bookmarks.tsx
+++ b/client/src/components/Bookmarks/Bookmarks.tsx
@@ -32,6 +32,15 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
const [formContentType, setFormContentType] = useState(ContentType.category);
const [isInEdit, setIsInEdit] = useState(false);
const [tableContentType, setTableContentType] = useState(ContentType.category);
+ const [isInUpdate, setIsInUpdate] = useState(false);
+ const [categoryInUpdate, setCategoryInUpdate] = useState
({
+ name: '',
+ id: -1,
+ isPinned: false,
+ bookmarks: [],
+ createdAt: new Date(),
+ updatedAt: new Date()
+ })
useEffect(() => {
if (props.categories.length === 0) {
@@ -45,6 +54,7 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
const addActionHandler = (contentType: ContentType) => {
setFormContentType(contentType);
+ setIsInUpdate(false);
toggleModal();
}
@@ -62,10 +72,21 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
}
}
+ const goToUpdateMode = (category: Category): void => {
+ setIsInUpdate(true);
+ setCategoryInUpdate(category);
+ toggleModal();
+ }
+
+ let modalForm: JSX.Element;
+
return (
-
+ {!isInUpdate
+ ?
+ :
+ }
{
: (!isInEdit
? props.categories.length > 0
?
- : You don't have any bookmarks. You can a new one from /bookmarks menu
- : )
+ : You don't have any bookmarks. You can add a new one from /bookmarks menu
+ : ()
+ )
}
)
diff --git a/client/src/components/UI/Buttons/Button/Button.module.css b/client/src/components/UI/Buttons/Button/Button.module.css
new file mode 100644
index 0000000..b9874e1
--- /dev/null
+++ b/client/src/components/UI/Buttons/Button/Button.module.css
@@ -0,0 +1,14 @@
+.Button {
+ padding: 8px 15px;
+ border: 1px solid var(--color-accent);
+ background-color: var(--color-background);
+ color: var(--color-primary);
+ border-radius: 4px;
+}
+
+.Button:hover,
+.Button:focus {
+ cursor: pointer;
+ background-color: var(--color-accent);
+ color: var(--color-background);
+}
\ No newline at end of file
diff --git a/client/src/components/UI/Buttons/Button/Button.tsx b/client/src/components/UI/Buttons/Button/Button.tsx
new file mode 100644
index 0000000..321e993
--- /dev/null
+++ b/client/src/components/UI/Buttons/Button/Button.tsx
@@ -0,0 +1,11 @@
+import classes from './Button.module.css';
+
+interface ComponentProps {
+ children: string;
+}
+
+const Button = (props: ComponentProps): JSX.Element => {
+ return
+}
+
+export default Button;
\ No newline at end of file
diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts
index db5f344..8b8b523 100644
--- a/client/src/store/actions/actionTypes.ts
+++ b/client/src/store/actions/actionTypes.ts
@@ -13,6 +13,7 @@ import {
AddBookmarkAction,
PinCategoryAction,
DeleteCategoryAction,
+ UpdateCategoryAction,
// Notifications
CreateNotificationAction,
ClearNotificationAction
@@ -38,6 +39,7 @@ export enum ActionTypes {
addBookmark = 'ADD_BOOKMARK',
pinCategory = 'PIN_CATEGORY',
deleteCategory = 'DELETE_CATEGORY',
+ updateCategory = 'UPDATE_CATEGORY',
// Notifications
createNotification = 'CREATE_NOTIFICATION',
clearNotification = 'CLEAR_NOTIFICATION'
@@ -58,6 +60,7 @@ export type Action =
AddBookmarkAction |
PinCategoryAction |
DeleteCategoryAction |
+ UpdateCategoryAction |
// 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 933be1b..279d740 100644
--- a/client/src/store/actions/bookmark.ts
+++ b/client/src/store/actions/bookmark.ts
@@ -147,4 +147,33 @@ export const deleteCategory = (id: number) => async (dispatch: Dispatch) => {
} catch (err) {
console.log(err);
}
+}
+
+/**
+ * UPDATE CATEGORY
+ */
+export interface UpdateCategoryAction {
+ type: ActionTypes.updateCategory,
+ payload: Category
+}
+
+export const updateCategory = (id: number, formData: NewCategory) => async (dispatch: Dispatch) => {
+ try {
+ const res = await axios.put>(`/api/categories/${id}`, formData);
+
+ dispatch({
+ type: ActionTypes.createNotification,
+ payload: {
+ title: 'Success',
+ message: `Category ${formData.name} updated`
+ }
+ })
+
+ dispatch({
+ type: ActionTypes.updateCategory,
+ 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 997d880..4a75491 100644
--- a/client/src/store/reducers/bookmark.ts
+++ b/client/src/store/reducers/bookmark.ts
@@ -78,6 +78,20 @@ const deleteCategory = (state: State, action: Action): State => {
}
}
+const updateCategory = (state: State, action: Action): State => {
+ const tmpCategories = [...state.categories];
+ const categoryInUpdate = tmpCategories.find((category: Category) => category.id === action.payload.id);
+
+ if (categoryInUpdate) {
+ categoryInUpdate.name = action.payload.name;
+ }
+
+ return {
+ ...state,
+ categories: tmpCategories
+ }
+}
+
const bookmarkReducer = (state = initialState, action: Action) => {
switch (action.type) {
case ActionTypes.getCategories: return getCategories(state, action);
@@ -86,6 +100,7 @@ const bookmarkReducer = (state = initialState, action: Action) => {
case ActionTypes.addBookmark: return addBookmark(state, action);
case ActionTypes.pinCategory: return pinCategory(state, action);
case ActionTypes.deleteCategory: return deleteCategory(state, action);
+ case ActionTypes.updateCategory: return updateCategory(state, action);
default: return state;
}
}