Skip to content

Commit 6d67e37

Browse files
authored
Merge pull request #72 from Lemoncode/feature/#65-reset-password-user-edit
feature/#65 reset user password
2 parents f63f0cc + 43651c8 commit 6d67e37

13 files changed

+171
-34
lines changed

src/common/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './form-field.hook';
2+
export * from './toogle.hook';

src/common/hooks/toogle.hook.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as React from 'react';
2+
3+
export const useToggle = (initialState: boolean) => {
4+
const [isOpen, setIsOpen] = React.useState(initialState);
5+
6+
const onToggle = () => setIsOpen(!isOpen);
7+
8+
return {
9+
isOpen,
10+
onToggle,
11+
setIsOpen,
12+
};
13+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as React from 'react';
2+
import Button from '@mui/material/Button';
3+
import Dialog from '@mui/material/Dialog';
4+
import DialogActions from '@mui/material/DialogActions';
5+
import DialogContent from '@mui/material/DialogContent';
6+
import DialogContentText from '@mui/material/DialogContentText';
7+
import DialogTitle from '@mui/material/DialogTitle';
8+
9+
interface Props {
10+
open: boolean;
11+
handleClose: () => void;
12+
}
13+
14+
export const ConfirmResetDialog: React.FC<Props> = props => {
15+
const { open, handleClose } = props;
16+
17+
return (
18+
<React.Fragment>
19+
<Dialog open={open} onClose={handleClose}>
20+
<DialogTitle id="alert-dialog-title">
21+
{'¿Está seguro que desea resetear la contraseña de este usuario?'}
22+
</DialogTitle>
23+
<DialogContent>
24+
<DialogContentText id="alert-dialog-description">
25+
Si está seguro de cambiar la contraseña presiona CONFIRMAR, de lo contrario presiona CANCELAR
26+
</DialogContentText>
27+
</DialogContent>
28+
<DialogActions>
29+
<Button onClick={handleClose}>Cancelar</Button>
30+
<Button onClick={handleClose} variant="contained" autoFocus>
31+
Confirmar
32+
</Button>
33+
</DialogActions>
34+
</Dialog>
35+
</React.Fragment>
36+
);
37+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './confirm-reset-dialog.component';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// TODO: ADD TESTS
2+
export const handleCopyPassword = (password: string) => navigator.clipboard.writeText(password);

src/modules/users/edit-reset-password/edit-reset-password.component.tsx

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,67 @@
11
import React from 'react';
2-
import * as innerClasses from './edit-reset-password.styles';
3-
import { useWithTheme } from '#core/theme';
4-
import { Button, IconButton, TextField } from '@mui/material';
2+
import { Form, Formik } from 'formik';
3+
import { Button, IconButton } from '@mui/material';
54
import { Visibility, VisibilityOff, ContentCopy } from '@mui/icons-material';
5+
import { TextFieldForm } from '#common/components';
6+
import { useToggle } from '#common/hooks';
7+
import { formValidation } from './validations';
68
import { usePassword } from '../create/use-password.hook';
9+
import { ConfirmResetDialog } from './components';
10+
import { handleCopyPassword } from './edit-reset-password.business';
11+
import { createEmptyInitialResetPassword } from './edit-reset-password.vm';
12+
import * as classes from './edit-reset-password.styles';
713

814
export const EditResetPasswordComponent: React.FC = () => {
915
const { showPassword, toggleShowPassword } = usePassword();
10-
const classes = useWithTheme(innerClasses);
16+
17+
const { isOpen, onToggle } = useToggle(false);
18+
1119
return (
1220
<div className={classes.root}>
1321
<div className={classes.sectionContainer}>
14-
<div className={classes.passwordFieldContainer}>
15-
<TextField
16-
name="contraseña"
17-
label="Nueva Contraseña"
18-
type={showPassword ? 'text' : 'password'}
19-
slotProps={{
20-
input: {
21-
endAdornment: (
22-
<IconButton onClick={toggleShowPassword}>
23-
{showPassword ? <VisibilityOff /> : <Visibility />}
24-
</IconButton>
25-
),
26-
},
27-
}}
28-
/>
29-
<IconButton onClick={() => {}} className={classes.icon}>
30-
<ContentCopy />
31-
</IconButton>
32-
</div>
22+
<Formik
23+
initialValues={createEmptyInitialResetPassword()}
24+
enableReinitialize={true}
25+
validate={formValidation.validateForm}
26+
onSubmit={() => {}}
27+
>
28+
{({ values, isValid, dirty }) => (
29+
<Form className={classes.formContainer}>
30+
<div className={classes.passwordFieldContainer}>
31+
<TextFieldForm
32+
name="contraseña"
33+
label="Contraseña"
34+
type={showPassword ? 'text' : 'password'}
35+
slotProps={{
36+
input: {
37+
endAdornment: (
38+
<IconButton onClick={toggleShowPassword}>
39+
{showPassword ? <VisibilityOff /> : <Visibility />}
40+
</IconButton>
41+
),
42+
},
43+
}}
44+
/>
45+
<IconButton
46+
onClick={() => {
47+
handleCopyPassword(values.contraseña || '');
48+
}}
49+
className={classes.icon}
50+
>
51+
<ContentCopy />
52+
</IconButton>
53+
</div>
54+
55+
<div className={classes.buttonContainer}>
56+
<Button type="button" variant="contained" disabled={!isValid || !dirty} onClick={onToggle}>
57+
Resetear Clave de Usuario
58+
</Button>
59+
</div>
60+
</Form>
61+
)}
62+
</Formik>
3363

34-
<div className={classes.buttonContainer}>
35-
<Button type="submit" variant="contained">
36-
Resetear Clave de Usuario
37-
</Button>
38-
</div>
64+
<ConfirmResetDialog open={isOpen} handleClose={onToggle} />
3965
</div>
4066
</div>
4167
);
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import React from 'react';
22
import { EditResetPasswordComponent } from './edit-reset-password.component';
33

4+
// TO DO: prop id se usará cuando el endpoint de reseteo de clave esté listo
45
interface Props {
56
id: string;
67
}
78

8-
export const EditResetPassword: React.FC<Props> = props => {
9-
const { id } = props;
10-
return <EditResetPasswordComponent id={id} />;
9+
export const EditResetPassword: React.FC<Props> = () => {
10+
// const { id } = props;
11+
return (
12+
<>
13+
<EditResetPasswordComponent />
14+
</>
15+
);
1116
};

src/modules/users/edit-reset-password/edit-reset-password.styles.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
11
import { css } from '@emotion/css';
2-
import { Theme } from '@mui/material';
2+
import { theme } from '#core/theme';
33

4-
export const root = (theme: Theme) => css`
4+
export const root = css`
55
width: 100%;
66
display: flex;
77
flex-direction: column;
88
gap: ${theme.spacing(4)};
99
`;
1010

11-
export const sectionContainer = (theme: Theme) => css`
11+
export const sectionContainer = css`
1212
border: 1px solid ${theme.palette.grey[300]};
1313
border-radius: 4px;
1414
padding: ${theme.spacing(3)};
1515
display: flex;
1616
flex-direction: column;
1717
justify-content: center;
18+
align-items: center;
1819
min-height: 200px;
1920
gap: 16px;
2021
`;
2122

23+
export const formContainer = css`
24+
display: flex;
25+
justify-content: center;
26+
flex-direction: column;
27+
gap: 16px;
28+
`;
29+
2230
export const passwordFieldContainer = css`
2331
display: flex;
2432
justify-content: center;
2533
gap: 16px;
34+
width: 400px;
2635
2736
@media (max-width: 768px) {
2837
grid-template-columns: 1fr;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface InitalResetPassword {
2+
contraseña: string;
3+
}
4+
5+
export const createEmptyInitialResetPassword = (): InitalResetPassword => ({
6+
contraseña: '',
7+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const validationMessages = {
2+
contraseña: { notValid: 'Introduce una contraseña de al menos 8 caracteres.' },
3+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createFormikValidation } from '@lemoncode/fonk-formik';
2+
import { validationMessages } from './edit-reset-password.literals';
3+
import { passwordValidation } from './password.validator';
4+
5+
const validationSchema = {
6+
field: {
7+
contraseña: [
8+
{
9+
validator: passwordValidation,
10+
message: validationMessages.contraseña.notValid,
11+
},
12+
],
13+
},
14+
};
15+
16+
export const formValidation = createFormikValidation(validationSchema);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './edit-reset-password.validations';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { FieldValidationFunctionAsync } from '@lemoncode/fonk';
2+
3+
const validatorType = 'CHECK_PASSWORD_CHARACTER';
4+
let defaultMessage = 'Contraseña debe tener al menos 8 caracteres';
5+
6+
export const passwordValidation: FieldValidationFunctionAsync = async fieldValidatorArgs => {
7+
const { value, message = defaultMessage } = fieldValidatorArgs;
8+
9+
const succeeded = typeof value === 'string' && value.length >= 8;
10+
11+
return {
12+
type: validatorType,
13+
succeeded: succeeded,
14+
message: succeeded ? '' : (message as string),
15+
};
16+
};

0 commit comments

Comments
 (0)