Skip to content
Merged
5 changes: 1 addition & 4 deletions src/api/endpoints/apiActions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { AxiosRequestConfig } from 'axios';
import { customInstance } from '../../../mock/mutator/customClient';
import { API_CONFIG } from '../../config';
import { useCookies } from 'react-cookie'


type SecondParameter<T extends (...args: any) => any> = Parameters<T>[1];

Expand Down Expand Up @@ -42,4 +39,4 @@ export const createGetRequest = <T = any, P = any>(endpoint: string, contentType
return response;
});
}
}
}
8 changes: 7 additions & 1 deletion src/api/endpoints/apiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export interface RegisterRequest {
organization: string
}

interface ForgotPasswordReguest {
username: string;
}

type LabelType =
| string
| { '@value': string; '@language'?: string }
Expand Down Expand Up @@ -210,6 +214,8 @@ export const retrieveTokenApi = ({ groupname }: { groupname: string }) => {
return createGetRequest<any, any>(endpoint, "application/json")();
};

export const forgotPassword = createPostRequest<any, ForgotPasswordReguest>(API_CONFIG.REAL_API.USER_RECOVER, { "Content-Type": "application/x-www-form-urlencoded" })

export const getMatchTerms = async (group: string, term: string, filters = {}) => {
try {
const response = await createGetRequest<any, any>(`/${group}/${term}.${BASE_EXTENSION}`, "application/json")();
Expand Down Expand Up @@ -244,4 +250,4 @@ export const getTermDiscussions = async (group: string, variantID: string) => {

export const getVariant = (group: string, term: string) => {
return createGetRequest<any, any>(`/${group}/variant/${term}`, "application/json")();
};
};
22 changes: 12 additions & 10 deletions src/components/Auth/ForgotPassword.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { Box, Button, FormControl, Grid, Paper, Typography } from "@mui/material
import { ArrowBack } from "@mui/icons-material";
import FormField from "./UI/Formfield";
import { Link } from "react-router-dom";
import { handleForgotPassword } from "../../api/endpoints/index";
import { forgotPassword } from "../../api/endpoints/apiService";

const ForgotPassword = () => {
const [email, setEmail] = React.useState("");
const [username, setUsername] = React.useState("");
const [error, setError] = React.useState("");

const forgotPassword = async () => {
const handleForgotPassword = async () => {
try {
await handleForgotPassword(email);
console.log("Password reset email sent");
await forgotPassword({username: username});
} catch (error) {
console.error("Error:", error);
setError(error.message);
}
};

Expand All @@ -29,14 +30,15 @@ const ForgotPassword = () => {
<form className="authForm">
<Grid container spacing={2.5}>
<FormField
label="Email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
label="Username"
placeholder="Enter your username"
value={username}
onChange={(e) => setUsername(e.target.value)}
errorMessage={error}
/>
<Grid item xs={12}>
<FormControl>
<Button variant="contained" color="primary" onClick={forgotPassword}>
<Button variant="contained" color="primary" onClick={handleForgotPassword}>
Reset my password
</Button>
</FormControl>
Expand Down
205 changes: 205 additions & 0 deletions src/components/Dashboard/User/AccountSettingsDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import PropTypes from "prop-types";
import { useState } from "react";
import { Box, Divider, Button, Typography, Avatar, Stack, Grid, TextField, Link } from "@mui/material";
import CustomizedDialog from "../../common/CustomizedDialog";
import PasswordField from "./PasswordField";
import PersonOutlineIcon from '@mui/icons-material/PersonOutline';
import ModeEditOutlineOutlinedIcon from "@mui/icons-material/ModeEditOutlineOutlined";
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';

import { vars } from "../../../theme/variables";
const { gray600, gray700, brand700 } = vars;

const HeaderRightSideContent = ({ handleClose, handleSubmit }) => {
return (
<Box display='flex' alignItems='center' gap='.75rem'>
<Button variant="outlined" onClick={handleClose}>Cancel</Button>
<Button variant='contained' disabled onClick={handleSubmit}>
Save Changes
</Button>
</Box>
);
};

HeaderRightSideContent.propTypes = {
handleClose: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
};

const AccountSettingsDialog = ({
user,
open,
handleClose
}) => {
const [showPasswordField, setShowPasswordField] = useState(false);
const [formData, setFormData] = useState({
email: user?.email,
currentPassword: "",
newPassword: "",
confirmPassword: "",
})

const handleInputChange = (field, value) => {
setFormData((prev) => ({
...prev,
[field]: value,
}))
}

const handleSubmit = (e) => {
e.preventDefault()
console.log("Form submitted:", formData)
}

const userGroupname = user?.groupname.charAt(0).toUpperCase() + user?.groupname.slice(1)
const isPasswordFormValid = formData.currentPassword.trim() !== "" && formData.newPassword.trim() !== "" && formData.confirmPassword.trim() !== "";


return (
<CustomizedDialog
open={open}
handleClose={handleClose}
title="Account settings"
aria-labelledby="accounts-settings-dialog-title"
fullScreen={false}
HeaderRightSideContent={<HeaderRightSideContent handleClose={handleClose} handleSubmit={handleClose} />}
sx={{
'& .MuiDialog-paper': {
maxWidth: '62.5rem'
}
}}
>
<Box display="flex" sx={{ alignItems: "center" }}>
<Avatar sx={{ width: 64, height: 64 }} >
<PersonOutlineIcon fontSize="large" />
</Avatar>
<Stack sx={{ marginLeft: "1.25rem" }}>
<Typography variant="h6" component="label" sx={{ color: "#3B403F" }}>{userGroupname}</Typography>
<Typography variant="subtitle1" sx={{ color: "#4D4F4F" }}>{user?.email}</Typography>
</Stack>
</Box>
<Divider sx={{ mt: 5, mb: 5 }} />
<Box component="form" onSubmit={handleSubmit}>
<Grid container spacing={4}>
<Grid item xs={12}>
<Box>
<Typography variant="subtitle2" component="label" sx={{ color: gray700, marginBottom: "0.375rem" }}>
Email
</Typography>
<TextField
fullWidth
type="email"
variant="outlined"
value={formData.email}
onChange={(e) => handleInputChange("email", e.target.value)}
disabled
size="small"
/>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "flex", alignItems: "center", fontSize: "0.875rem", color: gray600 }}>
Email address can&apos;t be changed from the interface. Please contact us to make this change.
<Link
href="mailto:support@interlex.org"
underline="none"
sx={{
display: 'inline-flex',
alignItems: 'center',
fontSize: '0.875rem',
fontWeight: 500,
color: brand700,
ml: 0.5
}}
>
Contact support
<ArrowForwardIcon fontSize="small" sx={{ ml: 0.5 }} />
</Link>
</Typography>
</Box>
</Grid>

{showPasswordField && (
<>
<Grid item xs={12}>
<Box>
<Typography variant="subtitle2" component="label" sx={{ color: gray700, marginBottom: "0.375rem" }}>
Current Password
</Typography>
<PasswordField
name="currentPassword"
value={formData.currentPassword}
handleChange={(e) => handleInputChange("currentPassword", e.target.value)}
placeholder="Enter your current password"
/>
</Box>
</Grid>

<Grid item xs={12} md={6}>
<Box>
<Typography variant="subtitle2" component="label" sx={{ color: gray700, marginBottom: "0.375rem" }}>
Enter New Password
</Typography>
<PasswordField
name="newPassword"
value={formData.newPassword}
handleChange={(e) => handleInputChange("newPassword", e.target.value)}
placeholder="Enter new password"
/>
</Box>
</Grid>

<Grid item xs={12} md={6}>
<Box>
<Typography variant="subtitle2" component="label" sx={{ color: gray700, marginBottom: "0.375rem" }}>
Confirm New Password
</Typography>
<PasswordField
name="confirmPassword"
value={formData.confirmPassword}
handleChange={(e) => handleInputChange("confirmPassword", e.target.value)}
placeholder="Confirm new password"
/>
</Box>
</Grid>
</>
)}

<Grid item xs={12}>
<Box sx={{ pt: 2 }}>
{!showPasswordField ? (
<Button
type="submit"
variant="outlined"
startIcon={<ModeEditOutlineOutlinedIcon />}
onClick={() => setShowPasswordField(true)}
>
Change Password
</Button>
) : (<>
<Button
type="submit"
variant="outlined"
startIcon={isPasswordFormValid ? <SaveOutlinedIcon /> : <ModeEditOutlineOutlinedIcon />}
onClick={() => setShowPasswordField(true)}
disabled={!isPasswordFormValid}
>
Save new password
</Button>
<Button variant="text" onClick={() => setShowPasswordField(false)}>
Cancel
</Button>
</>)}
</Box>
</Grid>
</Grid>
</Box>
</CustomizedDialog>
);
};

AccountSettingsDialog.propTypes = {
user: PropTypes.object.isRequired,
open: PropTypes.bool.isRequired,
handleClose: PropTypes.func.isRequired,
};

export default AccountSettingsDialog;
52 changes: 52 additions & 0 deletions src/components/Dashboard/User/PasswordField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useState } from "react";
import { Button, Typography } from "@mui/material";
import ActionInput from "../../common/ActionInput";
import { VisibilityOutlined, VisibilityOffOutlined } from "@mui/icons-material"
import PropTypes from "prop-types";

const PasswordField = ({ value, name, placeholder, handleChange, error, helperText }) => {
const [showPassword, setShowPassword] = useState(false)

return (
<div>
<ActionInput
placeholder={placeholder}
type={showPassword ? "text" : "password"}
name={name}
value={value}
onChange={handleChange}
size="small"
actionButton={
<Button
onClick={() => setShowPassword(!showPassword)}
startIcon={showPassword ? <VisibilityOutlined /> : <VisibilityOffOutlined />}
>
{showPassword ? "Show" : "Hide"}
</Button>
}
/>
{helperText && (
<Typography variant="caption" sx={{ mt: 1, display: "flex", alignItems: "center", fontSize: "0.875rem" }}>
{helperText}
</Typography>
)}
{error && <Typography variant="body2" sx={{ color: "#F04438", marginTop: "0.375rem" }}>{`${error.charAt(0).toUpperCase() + error.slice(1)}`}</Typography>}
</div>
)
}

PasswordField.propTypes = {
placeholder: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
error: PropTypes.bool,
helperText: PropTypes.string,
handleChange: PropTypes.func.isRequired
};

PasswordField.defaultProps = {
placeholder: "",
};


export default PasswordField;
11 changes: 8 additions & 3 deletions src/components/Dashboard/User/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {Box, Button, Chip, Grid, Stack, Typography} from "@mui/material";
import { useState } from "react";
import { Box, Button, Chip, Grid, Stack, Typography } from "@mui/material";
import { vars } from "../../../theme/variables";
import CustomBreadcrumbs from "../../common/CustomBreadcrumbs";
import HomeOutlinedIcon from "@mui/icons-material/HomeOutlined";
import { SettingsOutlined } from "@mui/icons-material";
import {GlobalDataContext} from "../../../contexts/DataContext";
import { GlobalDataContext } from "../../../contexts/DataContext";
import { useContext } from "react";
import AccountSettingsDialog from "./AccountSettingsDialog";

const { gray25, gray600, gray800, gray500 } = vars;

Expand All @@ -14,7 +16,8 @@ const breadcrumbItems = [
];

const User = () => {
const {user} = useContext(GlobalDataContext)
const { user } = useContext(GlobalDataContext)
const [open, setOpen] = useState(false);

return (
<Box sx={{
Expand All @@ -33,6 +36,7 @@ const User = () => {
<Button
startIcon={<SettingsOutlined />}
variant='outlined'
onClick={() => setOpen(true)}
>
Account settings
</Button>
Expand Down Expand Up @@ -69,6 +73,7 @@ const User = () => {
</Stack>
</Grid>
</Grid>
<AccountSettingsDialog user={user} open={open} handleClose={() => setOpen(false)} />
</Box>
);
};
Expand Down
Loading