Skip to content

Commit

Permalink
Refractor overlay and notification
Browse files Browse the repository at this point in the history
  • Loading branch information
WCY-dt committed May 5, 2024
1 parent 1156897 commit 9a299bd
Show file tree
Hide file tree
Showing 12 changed files with 216 additions and 177 deletions.
2 changes: 1 addition & 1 deletion src/components/clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface ClustersProps {

function Clusters({ dataKey }: ClustersProps) {
const {
needReload, setNeedReload,
needReload, setReload: setNeedReload,
selectedCategory,
searchTerm
} = useContext(AppContext);
Expand Down
71 changes: 39 additions & 32 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useContext } from 'react';
import React, { useEffect, useRef, useContext } from 'react';
import { Icon } from '@iconify/react';
import { Link } from 'react-router-dom';

Expand All @@ -8,15 +8,15 @@ import Login from '../popups/login';
import '../styles/components/header.css';

function Header() {
const {
isLogedIn, setIsLogedIn,
routes,
setToken,
setMessage,
searchTerm, setSearchTerm,
setShowOverlay,
setOverlayAction
} = useContext(AppContext);
const {
showLogin, setShowLogin,
isLogin, setLogin,
showMobileMenu, setShowMobileMenu,
routes,
setToken,
dispatchNotification,
searchTerm, setSearchTerm
} = useContext(AppContext);

const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
Expand All @@ -25,19 +25,27 @@ function Header() {
}
}, []);

const handleClearFilter = () => {
const onClickClearFilter = () => {
setSearchTerm('');
};

const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => setIsOpen(!isOpen);
const onClickOpenMenu = () => {
setShowMobileMenu(true);
}

const [showLogin, setShowLogin] = useState(false);
const toggleLogout = () => {
setIsLogedIn(false);
const onClickCloseMenu = () => {
setShowMobileMenu(false);
}

const onClickLogin = () => {
setShowLogin(true);
}

const onClickLogout = () => {
setLogin(false);
setToken('');
localStorage.removeItem('token');
setMessage('Successfully logged out');
dispatchNotification({ type: 'SUCCESS', message: 'Logout' });
};

return (
Expand All @@ -51,12 +59,12 @@ function Header() {
</div>
<ul className="App-nav-list">
{Object.entries(routes).map(([path, element]) => (
<li className="App-nav-item"><Link to={path}>{element as React.ReactNode}</Link></li>
<li className="App-nav-item"><Link to={path}>{element as React.ReactNode}</Link></li>
))}
</ul>
<div className="App-nav-right">
<div className={`App-nav-search ${isOpen ? 'open' : ''}`}>
{searchTerm ? <Icon className="App-nav-search-icon" icon="ci:close-md" onClick={handleClearFilter} /> : <Icon className="App-nav-search-icon" icon="ci:filter-outline" />}
<div className={`App-nav-search ${showMobileMenu ? 'open' : ''}`}>
{searchTerm ? <Icon className="App-nav-search-icon" icon="ci:close-md" onClick={onClickClearFilter} /> : <Icon className="App-nav-search-icon" icon="ci:filter-outline" />}
<input
ref={inputRef}
className="App-nav-search-input"
Expand All @@ -66,25 +74,24 @@ function Header() {
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<button className={`App-nav-login`} onClick={isLogedIn ? toggleLogout : () => {
setShowLogin(true);
setShowOverlay(true);
setOverlayAction(() => () => setShowLogin(false));
}} title="Login">{isLogedIn ? <Icon icon="ci:log-out" /> : <Icon icon="ci:user-01" />}</button>
{isLogin ?
<button type="button" className="App-nav-login" onClick={onClickLogout} title="Log out"><Icon icon="ci:log-out" /></button> :
<button type="button" className="App-nav-login" onClick={onClickLogin} title="Log in"><Icon icon="ci:user-01" /></button>
}
</div>
<button className="App-nav-button" onClick={toggleMenu} title="Open/Close navigator">{isOpen ? <Icon icon="ci:window-close" /> : <Icon icon="ci:window-sidebar" />}</button>
{isOpen && (
{showMobileMenu ?
<button className="App-nav-button" onClick={onClickCloseMenu} title="Close menu"><Icon icon="ci:window-close" /></button> :
<button className="App-nav-button" onClick={onClickOpenMenu} title="Open menu"><Icon icon="ci:window-sidebar" /></button>
}
{showMobileMenu && (
<ul className="App-nav-menu">
{Object.entries(routes).map(([path, element]) => (
<li className="App-nav-item"><Link to={path}>{element as React.ReactNode}</Link></li>
<li className="App-nav-item"><Link to={path}>{element as React.ReactNode}</Link></li>
))}
</ul>
)}
</nav>
<div className={`overlay ${isOpen ? 'open' : ''}`} onClick={() => {
if (isOpen) toggleMenu();
}}></div>
{showLogin && <Login setShowLogin={setShowLogin} /> }
{showLogin && <Login />}
</header>
)
}
Expand Down
19 changes: 7 additions & 12 deletions src/components/linkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ interface LinkCardProps {

function LinkCard({ item }: LinkCardProps) {
const {
isLogedIn,
isLogin,
setReload,
token,
setMessage,
dispatchNotification,
setShowConfirm,
setConfirmMessage,
setConfirmAction,
setSelectedCategory,
setShowOverlay,
setOverlayAction,
setShowEdit,
setEditContent,
setEditType
Expand All @@ -42,26 +41,22 @@ function LinkCard({ item }: LinkCardProps) {
setEditContent(item);
setEditType('modify');
setShowEdit(true);
setShowOverlay(true);
setOverlayAction(() => () => setShowEdit(false));
};

const tryDeleteCard = async (id: number) => {
const deleteCardResult = await deleteCardHandler({ id, token });

if (deleteCardResult === true) {
setMessage('Successfully deleted');
dispatchNotification({ type: 'SUCCESS', message: 'Card delete' });
} else {
setMessage('Failed to delete. Please try again.');
dispatchNotification({ type: 'ERROR', message: 'Card delete' });
}
};

const onClickDelete = () => {
setConfirmMessage('Are you sure to delete this card?');
setConfirmAction(() => () => tryDeleteCard(id));
setConfirmAction(() => () => {tryDeleteCard(id);});
setShowConfirm(true);
setShowOverlay(true);
setOverlayAction(() => () => setShowConfirm(false));
};

return (
Expand All @@ -81,7 +76,7 @@ function LinkCard({ item }: LinkCardProps) {
/>
</div>
</div>
{isLogedIn ? (
{isLogin ? (
<div className="link-card-edit">
<Icon icon="ci:edit-pencil-line-01" className="link-card-edit-edit" onClick={onClickEdit}></Icon>
<Icon icon="ci:trash-full" className="link-card-edit-delete" onClick={onClickDelete}></Icon>
Expand Down
69 changes: 44 additions & 25 deletions src/contexts/context.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
import React, { createContext, useState, useEffect } from 'react';
import React, { createContext, useState, useEffect, useReducer } from 'react';
import { fetchCollection } from '../services/collectionFetcher';

export type NotificationState = {
type: string;
message: string;
};
export type NotificationAction = {
type: 'SUCCESS' | 'ERROR';
message: string;
};
const notificationReducer = (state: NotificationState, action: NotificationAction) => {
switch (action.type) {
case 'SUCCESS':
return { ...state, type: 'SUCCESS', message: action.message };
case 'ERROR':
return { ...state, type: 'ERROR', message: action.message };
default:
return state;
}
}

export const AppContext = createContext({
isLoading: false,
setIsLoading: (_: boolean) => {},
isLogedIn: false,
setIsLogedIn: (_: boolean) => {},
setLoading: (_: boolean) => {},
showLogin: false,
setShowLogin: (_: boolean) => {},
isLogin: false,
setLogin: (_: boolean) => {},
showMobileMenu: false,
setShowMobileMenu: (_: boolean) => {},
needReload: false,
setNeedReload: (_: boolean) => {},
setReload: (_: boolean) => {},
routes: {},
setRoutes: (_: {[_: string]: React.ReactNode}) => {},
token: '',
setToken: (_: string) => {},
message: null as string | null,
setMessage: (_: string | null) => {},
notification: { type: '', message: '' },
dispatchNotification: (_: NotificationAction) => {},
showConfirm: false,
setShowConfirm: (_: boolean) => {},
confirmMessage: '',
Expand All @@ -26,43 +49,39 @@ export const AppContext = createContext({
setEditContent: (_: ClusterProps | null) => {},
editType: 'new' as 'new' | 'modify',
setEditType: (_: 'new' | 'modify') => {},
showOverlay: false,
setShowOverlay: (_: boolean) => {},
overlayAction: () => {},
setOverlayAction: (_: () => void) => {},
selectedCategory: null as string | null,
setSelectedCategory: (_: string | null) => {},
searchTerm: '',
setSearchTerm: (_: string) => {},
});

export const AppProvider = ({ children }: { children: React.ReactNode }) => {
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isLogedIn, setIsLogedIn] = useState<boolean>(false);
const [isLoading, setLoading] = useState<boolean>(true);
const [showLogin, setShowLogin] = useState<boolean>(false);
const [isLogin, setLogin] = useState<boolean>(false);
const [showMobileMenu, setShowMobileMenu] = useState<boolean>(false);
const [needReload, setNeedReload] = useState<boolean>(false);
const [routes, setRoutes] = useState({});
const [token, setToken] = useState<string>('');
const [message, setMessage] = useState<string | null>(null);
const [notification, dispatchNotification] = useReducer(notificationReducer, { type: '', message: '' });
const [showConfirm, setShowConfirm] = useState<boolean>(false);
const [confirmMessage, setConfirmMessage] = useState<string>('');
const [confirmAction, setConfirmAction] = useState<() => void>(() => () => {});
const [showEdit, setShowEdit] = useState<boolean>(false);
const [editContent, setEditContent] = useState<ClusterProps | null>(null);
const [editType, setEditType] = useState<'new' | 'modify'>('new');
const [showOverlay, setShowOverlay] = useState<boolean>(false);
const [overlayAction, setOverlayAction] = useState<() => void>(() => () => {});
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState('');

useEffect(() => {
setIsLoading(true);
setLoading(true);
fetchCollection(setRoutes)
.finally(() => setIsLoading(false));
.finally(() => setLoading(false));
}, []);

useEffect(() => {
if (localStorage.getItem('token')) {
setIsLogedIn(true);
setLogin(true);
setToken(localStorage.getItem('token') as string);
}
}, []);
Expand All @@ -73,20 +92,20 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {

return (
<AppContext.Provider value={{
isLoading, setIsLoading,
isLogedIn, setIsLogedIn,
needReload, setNeedReload,
isLoading, setLoading,
showLogin, setShowLogin,
isLogin, setLogin,
showMobileMenu, setShowMobileMenu,
needReload, setReload: setNeedReload,
routes, setRoutes,
token, setToken,
message, setMessage,
notification, dispatchNotification,
showConfirm, setShowConfirm,
confirmMessage, setConfirmMessage,
confirmAction, setConfirmAction,
showEdit, setShowEdit,
editContent, setEditContent,
editType, setEditType,
showOverlay, setShowOverlay,
overlayAction, setOverlayAction,
selectedCategory, setSelectedCategory,
searchTerm, setSearchTerm
}}>
Expand Down
27 changes: 13 additions & 14 deletions src/popups/confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ import '../styles/popups/confirm.css';

function Confirm() {
const {
setNeedReload,
setReload,
showConfirm, setShowConfirm,
confirmMessage,
confirmAction,
setShowOverlay
confirmAction
} = useContext(AppContext);

async function confirmHandler() {
await confirmAction();
setShowConfirm(false);
setShowOverlay(false);
setNeedReload(true);
}
const onClickConfirm = async () => {
await confirmAction();
setShowConfirm(false);
setReload(true);
}

const onClickCancel = () => {
setShowConfirm(false);
}

if (!showConfirm) {
return null;
Expand All @@ -29,11 +31,8 @@ function Confirm() {
<div className="Popup">
<div className="Confirm-message">{ confirmMessage }</div>
<div className="Confirm-button-pair">
<button type="button" className="Confirm-button Confirm-button-cancel" onClick={() => {
setShowConfirm(false);
setShowOverlay(false);
}}>cancel</button>
<button type="button" className="Confirm-button Confirm-button-ok" onClick={confirmHandler} >confirm</button>
<button type="button" className="Confirm-button Confirm-button-cancel" onClick={onClickCancel}>cancel</button>
<button type="button" className="Confirm-button Confirm-button-ok" onClick={onClickConfirm} >confirm</button>
</div>
</div>
</>
Expand Down
12 changes: 6 additions & 6 deletions src/popups/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ function Edit() {
routes,
showEdit, setShowEdit,
editContent,
editType,
setShowOverlay
editType
} = useContext(AppContext);

const onClickClose = () => {
setShowEdit(false);
}

if (!showEdit) {
return null;
}
Expand Down Expand Up @@ -63,10 +66,7 @@ function Edit() {
</div>
</div>
<button type="button" className="Edit-ok" title="Save edit">Save</button>
<button type="button" className="Edit-close" title="Close" onClick={() => {
setShowEdit(false);
setShowOverlay(false);
}}>
<button type="button" className="Edit-close" title="Close" onClick={onClickClose}>
<Icon icon="ci:close-md" />
</button>
</div>
Expand Down
Loading

0 comments on commit 9a299bd

Please sign in to comment.