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; } }