-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
313 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,21 @@ | ||
import React, { createContext, useMemo, useState, useContext } from 'react'; | ||
// src/ThemeContext.js | ||
import React, { createContext, useMemo, useState, useContext, useEffect } from 'react'; | ||
import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; | ||
import { lightTheme, darkTheme } from './theme'; | ||
import { useSelector } from 'react-redux'; | ||
|
||
const ThemeContext = createContext(); | ||
|
||
export const ThemeProvider = ({ children }) => { | ||
const [mode, setMode] = useState('light'); | ||
const mode = useSelector((state) => state.user.theme); | ||
const theme = useMemo(() => (mode === 'light' ? lightTheme : darkTheme), [mode]); | ||
|
||
const toggleTheme = () => { | ||
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); | ||
}; | ||
|
||
return ( | ||
<ThemeContext.Provider value={{ mode, toggleTheme }}> | ||
<ThemeContext.Provider value={{ mode }}> | ||
<MuiThemeProvider theme={theme}>{children}</MuiThemeProvider> | ||
</ThemeContext.Provider> | ||
); | ||
}; | ||
|
||
export const useTheme = () => useContext(ThemeContext); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import React, { useState } from 'react'; | ||
import { Modal, Box, Typography, TextField, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { updateUserProfileAsync } from '../features/user/userSlice'; | ||
|
||
const ProfileModal = ({ open, handleClose }) => { | ||
const dispatch = useDispatch(); | ||
const { userInfo } = useSelector((state) => state.user); | ||
const [name, setName] = useState(userInfo.name); | ||
const [email, setEmail] = useState(userInfo.email); | ||
const [password, setPassword] = useState(''); | ||
const [confirmPassword, setConfirmPassword] = useState(''); | ||
const [confirmationOpen, setConfirmationOpen] = useState(false); | ||
const [message, setMessage] = useState(''); | ||
|
||
const handleSave = () => { | ||
if (password !== confirmPassword) { | ||
setMessage('Passwords do not match!'); | ||
return; | ||
} | ||
setMessage(''); | ||
setConfirmationOpen(true); | ||
}; | ||
|
||
const handleConfirmSave = () => { | ||
const updatedProfile = { | ||
name, | ||
email, | ||
password: password ? password : undefined, | ||
}; | ||
dispatch(updateUserProfileAsync(updatedProfile)); | ||
setConfirmationOpen(false); | ||
handleClose(); | ||
}; | ||
|
||
const handleCancel = () => { | ||
setConfirmationOpen(false); | ||
}; | ||
|
||
return ( | ||
<> | ||
<Modal | ||
open={open} | ||
onClose={handleClose} | ||
aria-labelledby="profile-modal-title" | ||
aria-describedby="profile-modal-description" | ||
> | ||
<Box | ||
sx={{ | ||
position: 'absolute', | ||
top: '50%', | ||
left: '50%', | ||
transform: 'translate(-50%, -50%)', | ||
width: 400, | ||
bgcolor: 'background.paper', | ||
border: '2px solid #000', | ||
boxShadow: 24, | ||
p: 4, | ||
}} | ||
> | ||
<Typography id="profile-modal-title" variant="h6" component="h2"> | ||
Update Profile | ||
</Typography> | ||
{message && ( | ||
<Typography color="error" sx={{ mt: 1 }}> | ||
{message} | ||
</Typography> | ||
)} | ||
<TextField | ||
label="Name" | ||
fullWidth | ||
margin="normal" | ||
value={name} | ||
onChange={(e) => setName(e.target.value)} | ||
/> | ||
<TextField | ||
label="Email" | ||
fullWidth | ||
margin="normal" | ||
value={email} | ||
onChange={(e) => setEmail(e.target.value)} | ||
/> | ||
<TextField | ||
label="Password" | ||
fullWidth | ||
margin="normal" | ||
type="password" | ||
value={password} | ||
onChange={(e) => setPassword(e.target.value)} | ||
/> | ||
<TextField | ||
label="Confirm Password" | ||
fullWidth | ||
margin="normal" | ||
type="password" | ||
value={confirmPassword} | ||
onChange={(e) => setConfirmPassword(e.target.value)} | ||
/> | ||
<Button | ||
variant="contained" | ||
color="primary" | ||
onClick={handleSave} | ||
sx={{ mt: 2 }} | ||
> | ||
Save | ||
</Button> | ||
</Box> | ||
</Modal> | ||
|
||
<Dialog | ||
open={confirmationOpen} | ||
onClose={handleCancel} | ||
aria-labelledby="confirm-dialog-title" | ||
aria-describedby="confirm-dialog-description" | ||
> | ||
<DialogTitle id="confirm-dialog-title">Confirm Profile Update</DialogTitle> | ||
<DialogContent> | ||
<DialogContentText id="confirm-dialog-description"> | ||
Are you sure you want to update your profile details? | ||
</DialogContentText> | ||
</DialogContent> | ||
<DialogActions> | ||
<Button onClick={handleCancel} color="secondary"> | ||
Cancel | ||
</Button> | ||
<Button onClick={handleConfirmSave} color="primary" autoFocus> | ||
Confirm | ||
</Button> | ||
</DialogActions> | ||
</Dialog> | ||
</> | ||
); | ||
}; | ||
|
||
export default ProfileModal; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,78 @@ | ||
// src/components/Sidebar.js | ||
import React from 'react'; | ||
import { Drawer, List, ListItem, ListItemIcon, ListItemText, Toolbar } from '@mui/material'; | ||
import DashboardIcon from '@mui/icons-material/Dashboard'; | ||
import ReceiptIcon from '@mui/icons-material/Receipt'; | ||
import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; | ||
import PieChartIcon from '@mui/icons-material/PieChart'; | ||
import BarChartIcon from '@mui/icons-material/BarChart'; | ||
import { Drawer, List, ListItem, ListItemIcon, ListItemText, Toolbar, Typography } from '@mui/material'; | ||
import { Dashboard as DashboardIcon, Receipt as ReceiptIcon, MonetizationOn as MonetizationOnIcon, PieChart as PieChartIcon, BarChart as BarChartIcon } from '@mui/icons-material'; | ||
import { Link } from 'react-router-dom'; | ||
import { useTheme } from '@mui/material/styles'; | ||
|
||
const Sidebar = () => { | ||
const drawerWidth = 240; | ||
const theme = useTheme(); | ||
|
||
return ( | ||
<Drawer | ||
variant="permanent" | ||
sx={{ | ||
width: drawerWidth, | ||
flexShrink: 0, | ||
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box' }, | ||
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box', bgcolor: theme.palette.background.default }, | ||
}} | ||
> | ||
<Toolbar /> | ||
<List> | ||
<ListItem component={Link} to="/dashboard"> | ||
<ListItem component={Link} to="/dashboard" sx={{ py: 1 }}> | ||
<ListItemIcon><DashboardIcon /></ListItemIcon> | ||
<ListItemText primary="Dashboard" /> | ||
<ListItemText | ||
primary={ | ||
<Typography variant="body1" style={{ color: theme.palette.text.primary }}> | ||
Dashboard | ||
</Typography> | ||
} | ||
/> | ||
</ListItem> | ||
<ListItem component={Link} to="/expenses"> | ||
<ListItem component={Link} to="/expenses" sx={{ py: 1 }}> | ||
<ListItemIcon><ReceiptIcon /></ListItemIcon> | ||
<ListItemText primary="Expenses" /> | ||
<ListItemText | ||
primary={ | ||
<Typography variant="body1" style={{ color: theme.palette.text.primary }}> | ||
Expenses | ||
</Typography> | ||
} | ||
/> | ||
</ListItem> | ||
<ListItem component={Link} to="/income"> | ||
<ListItem component={Link} to="/income" sx={{ py: 1 }}> | ||
<ListItemIcon><MonetizationOnIcon /></ListItemIcon> | ||
<ListItemText primary="Income" /> | ||
<ListItemText | ||
primary={ | ||
<Typography variant="body1" style={{ color: theme.palette.text.primary }}> | ||
Income | ||
</Typography> | ||
} | ||
/> | ||
</ListItem> | ||
<ListItem component={Link} to="/budget"> | ||
<ListItem component={Link} to="/budget" sx={{ py: 1 }}> | ||
<ListItemIcon><PieChartIcon /></ListItemIcon> | ||
<ListItemText primary="Budget" /> | ||
<ListItemText | ||
primary={ | ||
<Typography variant="body1" style={{ color: theme.palette.text.primary }}> | ||
Budget | ||
</Typography> | ||
} | ||
/> | ||
</ListItem> | ||
<ListItem component={Link} to="/reports"> | ||
<ListItem component={Link} to="/reports" sx={{ py: 1 }}> | ||
<ListItemIcon><BarChartIcon /></ListItemIcon> | ||
<ListItemText primary="Reports" /> | ||
<ListItemText | ||
primary={ | ||
<Typography variant="body1" style={{ color: theme.palette.text.primary }}> | ||
Reports | ||
</Typography> | ||
} | ||
/> | ||
</ListItem> | ||
</List> | ||
</Drawer> | ||
); | ||
}; | ||
|
||
export default Sidebar; | ||
|
Oops, something went wrong.