diff --git a/src/components/emails/compose-modal.tsx b/src/components/emails/compose-modal.tsx index bd93222..8862cc1 100644 --- a/src/components/emails/compose-modal.tsx +++ b/src/components/emails/compose-modal.tsx @@ -1,71 +1,63 @@ -import React, { Dispatch } from 'react'; +import React, { Dispatch } from 'react'; import { Select, Form, Input, Button } from 'antd'; -import {EmailCreation, User} from '../../types'; +import { EmailCreation, User } from '../../types'; import { connect } from 'react-redux'; -import {sendEmail} from '../../store/emails/actions'; -import {ActionTypes} from '../../store/emails/types'; - +import { sendEmail } from '../../store/emails/actions'; +import { ActionTypes } from '../../store/emails/types'; interface ComposeModal { - sendEmail : (data:EmailCreation) => void, - modalInfo ?: any, - emailSuccessMessage : string, - currentUser : User + sendEmail: (data: EmailCreation) => void; + modalInfo?: any; + emailSuccessMessage: string; + currentUser: User; } -function ComposeModal({sendEmail, currentUser}:ComposeModal) { - const {email} = currentUser; - - const onFinish = (values:EmailCreation) => { - if(!values.body.trim()){ - if(!window.confirm("Do you want to proceed without body ?")){ +function ComposeModal({ sendEmail, currentUser }: ComposeModal) { + const { email } = currentUser; + + const onFinish = (values: EmailCreation) => { + if (!values.body?.trim()) { + if (!window.confirm('Do you want to proceed without body ?')) { return false; } } - sendEmail({...values, sender : email}); + sendEmail({ ...values, sender: email }); }; return ( -
onFinish(values)}> - - - - - - - - - - - - - - - -
) +
onFinish(values)}> + + + + + + + + + + + + +
+ ); } - -const mapStateToProps = ({email, auth}:any) => { - +const mapStateToProps = ({ email, auth }: any) => { return { - currentUser : auth.currentUser, - emailSuccessMessage : email.successMessage - } -} + currentUser: auth.currentUser, + emailSuccessMessage: email.successMessage, + }; +}; -const mapDispatchToProps = (dispatch:Dispatch) => { +const mapDispatchToProps = (dispatch: Dispatch) => { return { - sendEmail : (data:EmailCreation) => dispatch(sendEmail(data)), + sendEmail: (data: EmailCreation) => dispatch(sendEmail(data)), }; }; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ComposeModal); \ No newline at end of file +export default connect(mapStateToProps, mapDispatchToProps)(ComposeModal); \ No newline at end of file diff --git a/src/components/emails/list.tsx b/src/components/emails/list.tsx index 944f5c7..603d334 100644 --- a/src/components/emails/list.tsx +++ b/src/components/emails/list.tsx @@ -1,68 +1,66 @@ -import React, { useState, Dispatch, useEffect } from 'react'; +import React, { useState, Dispatch } from 'react'; import { Table, Button } from 'antd'; -import { Redirect, useParams, useHistory } from 'react-router-dom'; +import { useParams, useNavigate } from 'react-router-dom'; import { connect } from 'react-redux'; -import {includes} from 'lodash'; +import { includes } from 'lodash'; import './list.css'; -import {ParamTypes} from '../../types'; -import {emailListColumns, emailListFirstColumn} from './config'; -import {deleteEmail} from '../../store/emails/actions'; -import {ActionTypes} from '../../store/emails/types'; +import { emailListColumns, emailListFirstColumn } from './config'; +import { deleteEmail } from '../../store/emails/actions'; +import { ActionTypes } from '../../store/emails/types'; -function EmailList({emails, deleteEmail}:any) { - const rhist:any = useHistory(); - let { emailAction } = useParams(); - const firstColumn = emailListFirstColumn[emailAction] || {}; - const [selectedRowKeys, setSelectedRowKeys] = useState([]); - const onSelectChange = (selectedRowKeys:any) => { - setSelectedRowKeys(selectedRowKeys); - } +function EmailList({ emails, deleteEmail }: any) { + const navigate = useNavigate(); + let { emailAction } = useParams<{ emailAction: string }>(); + const firstColumn = emailListFirstColumn[emailAction!] || {}; + const [selectedRowKeys, setSelectedRowKeys] = useState([]); - const deleteAnEmail = () => { - const emailList = emails - .filter((email:any, index:number) => includes(selectedRowKeys, index)) - .map(({emailUuid}:any) => emailUuid) - deleteEmail(emailList); - setSelectedRowKeys([]); - } - - + const onSelectChange = (selectedRowKeys: any) => { + setSelectedRowKeys(selectedRowKeys); + }; + const deleteAnEmail = () => { + const emailList = emails + .filter((email: any, index: number) => includes(selectedRowKeys, index)) + .map(({ emailUuid }: any) => emailUuid); + deleteEmail(emailList); + setSelectedRowKeys([]); + }; - return ( - <> - - (readClass || "")} - rowKey={({index}) => index} - onRow={({emailUuid}, rowIndex) => { - return { - onClick: event => rhist.push(`/dashboard/${emailAction}/view/${emailUuid}`) - } - }} - columns={[firstColumn, ...emailListColumns]} dataSource={emails} /> - - ); + return ( + <> + +
readClass || ""} + rowKey={({ index }) => index} + onRow={({ emailUuid }, rowIndex) => ({ + onClick: event => navigate(`/dashboard/${emailAction}/view/${emailUuid}`) + })} + columns={[firstColumn, ...emailListColumns]} + dataSource={emails} + /> + + ); } +const mapDispatchToProps = (dispatch: Dispatch) => { + return { + deleteEmail: (emailUuids: string[]) => dispatch(deleteEmail(emailUuids)), + }; +}; -const mapDispatchToProps = (dispatch:Dispatch) => { +const mapStateToProps = ({ email, auth }: any) => { return { - deleteEmail : (emailUuids:string[]) => dispatch(deleteEmail(emailUuids)), + currentUser: auth.currentUser, + emails: email.emails, }; }; -const mapStateToProps = ({email, auth}:any) => { - return { - currentUser : auth.currentUser, - emails : email.emails, - } -} -export default connect( - mapStateToProps, - mapDispatchToProps -)(EmailList); +export default connect(mapStateToProps, mapDispatchToProps)(EmailList); \ No newline at end of file diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index e8f9948..d467fcd 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -1,6 +1,6 @@ import React, {useEffect, Dispatch, useState} from 'react'; import { connect } from 'react-redux'; -import { Redirect, useParams,Switch, Route } from 'react-router-dom'; +import { Navigate, useParams, Routes, Route } from 'react-router-dom'; import EmailList from '../../components/emails/list'; import EmailOps from '../../components/emails/ops'; import EmailView from '../../components/emails/view'; @@ -14,104 +14,105 @@ import * as AuthTypes from '../../store/auth/types'; import {isEmpty} from 'lodash'; import {getEmails} from '../../store/emails/actions'; import {ActionTypes} from '../../store/emails/types'; -import {User, ParamTypes} from '../../types'; -import { Button, Layout, Menu , message} from 'antd'; +import {User} from '../../types'; +import { Button, Layout, message } from 'antd'; import { MailOutlined } from '@ant-design/icons'; import './style.css'; const { Header, Content, Footer } = Layout; interface dashboardProps { - getEmailsUpdate : (email:string, emailAction?:string) => void, - successMessage : string, - errorMessage : string, - reset : () => void, - signOut: (currentUser:User) => void -}; - + getEmailsUpdate: (email: string, emailAction?: string) => void, + successMessage: string, + errorMessage: string, + reset: () => void, + signOut: (currentUser: User) => void +} -function Dashboard({unreadCount, match, signOut, currentUser, emails, successMessage, errorMessage, reset, getEmailsUpdate}:any) { +function Dashboard({unreadCount, signOut, currentUser, emails, successMessage, errorMessage, reset, getEmailsUpdate}: any) { const [login, goToLogin] = useState(false); - let { emailAction} = useParams(); - useEffect(()=>{ - if(successMessage){ + let { emailAction } = useParams<{emailAction: string}>(); + + // ✅ Fix: extract emailAction from URL path if useParams returns undefined + const pathEmailAction = emailAction || window.location.pathname.split('/')[2]; + + useEffect(() => { + if (successMessage) { message.success(successMessage); - return reset() + reset(); } - if(errorMessage){ - message.success(errorMessage); - return reset() + + if (errorMessage) { + message.error(errorMessage); + reset(); } - if(!isEmpty(currentUser)){ - getEmailsUpdate(currentUser.email, emailAction); - }else{ + if (!isEmpty(currentUser)) { + // ✅ Fix: use pathEmailAction instead of emailAction + getEmailsUpdate(currentUser.email, pathEmailAction); + } else { goToLogin(true); } - }) + // ✅ Fix: use pathEmailAction in dependency array + }, [currentUser, pathEmailAction, successMessage, errorMessage]); const getOut = () => { signOut(currentUser); } - if(login){ - return + if (login) { + return ; } - console.log("match", match); + return ( -
- - (
- - {unreadCount} -
) -
+
+ +
+ + {unreadCount} +
+
- -
- -
-
- - - - - - - - -
+ +
+ {/* ✅ Fix: pass pathEmailAction instead of emailAction */} + +
+
+ + } /> + } /> + +
Created by bron10
- ); } -const mapStateToProps = ({email, notification, auth}:any) => { +const mapStateToProps = ({email, notification, auth}: any) => { return { - currentUser : auth.currentUser, - successMessage : notification.successMessage, - errorMessage : notification.errorMessage, - unreadCount : email.unreadCount + currentUser: auth.currentUser, + successMessage: notification.successMessage, + errorMessage: notification.errorMessage, + unreadCount: email.unreadCount } } -const mapDispatchToProps = (dispatch:Dispatch) => { +const mapDispatchToProps = (dispatch: Dispatch) => { return { - reset : () => dispatch(reset()), - signOut : (data:User) => dispatch(signOut(data)), - getEmailsUpdate: (email:string, emailAction?:string) => dispatch(getEmails(email, emailAction)) + reset: () => dispatch(reset()), + signOut: (data: User) => dispatch(signOut(data)), + getEmailsUpdate: (email: string, emailAction?: string) => dispatch(getEmails(email, emailAction)) }; }; - export default connect( mapStateToProps, mapDispatchToProps -)(Dashboard); +)(Dashboard); \ No newline at end of file diff --git a/src/sagas/auth.ts b/src/sagas/auth.ts index 00483d3..6200ec1 100644 --- a/src/sagas/auth.ts +++ b/src/sagas/auth.ts @@ -1,27 +1,29 @@ import { put, call, take, takeLatest} from 'redux-saga/effects'; +import { SagaIterator } from 'redux-saga'; import {START_LOGIN, START_SIGNOUT} from '../store/auth/types'; import {signInSuccess, signOutSuccess} from '../store/auth/actions'; import {notifyError, notify} from '../store/notification/actions'; import {login, signout} from '../services/api'; import {Credentials, EmailCreation, User} from '../types' + const ANY_ERROR_TEXT = 'Something went wrong, please try again'; -function* loginUpdates({credentials}: any) { +function* loginUpdates({credentials}: any): SagaIterator { try{ - // console.log("payload", payload); const currentUser = yield call(login, credentials) yield put(signInSuccess(currentUser)); - }catch({message}){ + }catch(e){ + const message = (e as Error).message; yield put(notifyError(message || ANY_ERROR_TEXT)); } } -function* signOutUpdates({currentUser}: any) { +function* signOutUpdates({currentUser}: any): SagaIterator { try{ - // console.log("payload", payload); const response = yield call(signout, currentUser) yield put(signOutSuccess()); - }catch({message}){ + }catch(e){ + const message = (e as Error).message; yield put(notifyError(message || ANY_ERROR_TEXT)); } } @@ -30,5 +32,3 @@ export function* watchAuth() { yield takeLatest(START_LOGIN, loginUpdates) yield takeLatest(START_SIGNOUT, signOutUpdates) } - - diff --git a/src/sagas/email.ts b/src/sagas/email.ts index 16eadab..b21dbb9 100644 --- a/src/sagas/email.ts +++ b/src/sagas/email.ts @@ -1,47 +1,42 @@ -import { put, call, take, takeLatest} from 'redux-saga/effects'; - +import { put, call, takeLatest } from 'redux-saga/effects'; +import { SagaIterator } from 'redux-saga'; import {GET_EMAILS_START, SEND_EMAIL_START, DELETE_EMAIL, SET_EMAIL_READ} from '../store/emails/types'; -import {getEmailsSuccess, deleteEmailSuccess} from '../store/emails/actions'; +import {getEmailsSuccess, deleteEmailSuccess, sendEmailSuccess, getEmails} from '../store/emails/actions'; import {notify} from '../store/notification/actions'; import {getAccountEmails, sendAccountEmail, deleteEmails, setEmailRead} from '../services/api'; -import {EmailCreation} from '../types' -function* getEmailUpdates({email, emailAction}:any) { +function* getEmailUpdates({email, emailAction}:any): SagaIterator { try{ const response = yield call(getAccountEmails, email, emailAction) yield put(getEmailsSuccess(response)); - }catch(e){ - } } -function* sendEmailUpdates({payload}: any){ +function* sendEmailUpdates({payload}: any): SagaIterator { try{ const response = yield call(sendAccountEmail, payload) - yield put(notify(response.message)); + yield put(notify(response.message)); + yield put(sendEmailSuccess(response.message)); + yield put(getEmails(payload.sender, 'send')); }catch(e){ - // yield put(getError(response)); } } - -function* deleteEmailUpdates({emailUuids}: any){ +function* deleteEmailUpdates({emailUuids}: any): SagaIterator { try{ const response = yield call(deleteEmails, emailUuids) yield put(notify(response.message)); yield put(getEmailsSuccess(response.emails)); }catch(e){ - // yield put(getError(response)); } } -function* setEmailreadUpdates({emailUuid}:any){ +function* setEmailreadUpdates({emailUuid}:any): SagaIterator { try{ const response = yield call(setEmailRead, emailUuid) yield put(getEmailsSuccess(response.emails)); }catch(e){ - // yield put(getError(response)); } } @@ -50,6 +45,4 @@ export function* watchEmail() { yield takeLatest(SEND_EMAIL_START, sendEmailUpdates) yield takeLatest(DELETE_EMAIL, deleteEmailUpdates) yield takeLatest(SET_EMAIL_READ, setEmailreadUpdates) -} - - +} \ No newline at end of file diff --git a/src/sagas/index.ts b/src/sagas/index.ts index 12bdad0..06f5bf9 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -1,7 +1,7 @@ -import { all } from 'redux-saga/effects'; +import { all, fork } from 'redux-saga/effects'; import { watchEmail } from './email'; import {watchAuth} from './auth'; -export default function* rootSaga() { - return yield all([watchEmail(), watchAuth()]); +export default function* rootSaga() { + yield all([fork(watchEmail), fork(watchAuth)]); } \ No newline at end of file diff --git a/src/services/api.ts b/src/services/api.ts index 61a22f3..a5886c1 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,8 +1,8 @@ -import { isJSDocPublicTag } from 'typescript'; import {Credentials, Email, EmailCreation, User} from '../types' import {includes} from 'lodash'; + export async function getAccountEmails(email:string, emailAction?:string){ - let emails = localStorage.getItem('emails'); + let emails = localStorage.getItem('emails'); if(emails && emails.length){ const parsedEmails = JSON.parse(emails); console.log("emailAction", emailAction); @@ -10,84 +10,89 @@ export async function getAccountEmails(email:string, emailAction?:string){ case 'send': return parsedEmails.filter(({sender}: any) => email === sender); default: - return parsedEmails.filter(({to}: any) => includes(to, email)); + return parsedEmails.filter(({to}: any) => includes(to, email)); } } return []; } export async function sendAccountEmail(email: EmailCreation){ - let emails = localStorage.getItem('emails'); - console.log("ermead", emails) - if(emails && emails.length){ - const parsedEmails = JSON.parse(emails); - if(parsedEmails.length){ - let emailUuid = parsedEmails[0] ? (parsedEmails[0].emailUuid + 1) : 1234; - parsedEmails.unshift({...email, read: false, created_at : Date.now(), emailUuid : emailUuid}) - } - localStorage.emails = JSON.stringify(parsedEmails); - return { - message : "Email send successfully", - emails - }; - // return emails.filter(({to}:any) => to === emails) - } - return []; + let emails = localStorage.getItem('emails'); + + // ✅ Fix: handle both empty and non-empty cases + const parsedEmails = emails && emails.length ? JSON.parse(emails) : []; + + let emailUuid = parsedEmails.length > 0 ? (parsedEmails[0].emailUuid + 1) : 1234; + parsedEmails.unshift({ + ...email, + read: false, + created_at: Date.now(), + emailUuid: emailUuid + }); + + // ✅ Fix: always save back to localStorage + localStorage.setItem('emails', JSON.stringify(parsedEmails)); + + return { + message: "Email sent successfully", + emails: parsedEmails + }; } export async function login(credentials: Credentials){ - let users = localStorage.getItem('users'); + let users = localStorage.getItem('users'); if(users && users.length){ const parsedUsers = JSON.parse(users); if(users.length){ - const selectedUser = parsedUsers.find(({email, password}: User) => credentials.email === email && credentials.password === password) + const selectedUser = parsedUsers.find(({email, password}: User) => + credentials.email === email && credentials.password === password + ) if(selectedUser){ - localStorage.currentUser = JSON.stringify(selectedUser); - return Promise.resolve(selectedUser); + localStorage.currentUser = JSON.stringify(selectedUser); + return Promise.resolve(selectedUser); } } } - return Promise.reject({ - message : 'User not found' + message: 'User not found' }) } export async function signout(currentUser: User){ delete localStorage.currentUser; - return {message : "Logout successfully"}; + return {message: "Logout successfully"}; } -export async function deleteEmails(uuids : string[]){ - let emails = localStorage.getItem('emails'); +export async function deleteEmails(uuids: string[]){ + let emails = localStorage.getItem('emails'); if(emails && emails.length){ const parsedEmails = JSON.parse(emails); - const newList = parsedEmails.filter(({emailUuid}:any) => !includes(uuids, emailUuid)); - localStorage.emails = JSON.stringify(newList) + const newList = parsedEmails.filter(({emailUuid}:any) => !includes(uuids, emailUuid)); + localStorage.setItem('emails', JSON.stringify(newList)); return { - message : "Deleted email successfully", - emails : newList + message: "Deleted email successfully", + emails: newList } } return Promise.reject({ - message : 'Email not found' + message: 'Email not found' }); } export async function setEmailRead(uuid:string){ - let emails = localStorage.getItem('emails'); + let emails = localStorage.getItem('emails'); if(emails && emails.length){ const parsedEmails = JSON.parse(emails); - const list = parsedEmails.map(({emailUuid, ...otherInfo}:any) => { + const list = parsedEmails.map(({emailUuid, ...otherInfo}:any) => { if(emailUuid == uuid){ otherInfo.read = true; } return {emailUuid, ...otherInfo}; }); - localStorage.emails = JSON.stringify(list); - return {emails : list} + localStorage.setItem('emails', JSON.stringify(list)); + return {emails: list} } return Promise.reject({ - message : 'Email not found' + message: 'Email not found' }); } \ No newline at end of file