-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(profil): change password (#879)
- Loading branch information
Showing
9 changed files
with
261 additions
and
32 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
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,21 @@ | ||
import React from 'react' | ||
import ProfilLayout from '@/components/layouts/ProfilLayout' | ||
import * as metatags from '@/config/metatags' | ||
import ChangePassScreen from '@/screens/profil/password/page' | ||
import Head from 'expo-router/head' | ||
|
||
function ChangePasswordScreen() { | ||
return ( | ||
<> | ||
<Head> | ||
<title>{metatags.createTitle('Cotisation et dons')}</title> | ||
</Head> | ||
|
||
<ProfilLayout> | ||
<ChangePassScreen /> | ||
</ProfilLayout> | ||
</> | ||
) | ||
} | ||
|
||
export default ChangePasswordScreen |
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,164 @@ | ||
import { useMemo } from 'react' | ||
import { KeyboardAvoidingView, Platform } from 'react-native' | ||
import Input from '@/components/base/Input/Input' | ||
import Text from '@/components/base/Text' | ||
import { VoxButton } from '@/components/Button' | ||
import PageLayout from '@/components/layouts/PageLayout/PageLayout' | ||
import VoxCard from '@/components/VoxCard/VoxCard' | ||
import { ProfilChangePasswordFormError } from '@/services/profile/error' | ||
import { usetPostChangePassword } from '@/services/profile/hook' | ||
import { zodResolver } from '@hookform/resolvers/zod' | ||
import { Controller, useForm } from 'react-hook-form' | ||
import { isWeb, ScrollView, useMedia, XStack, YStack } from 'tamagui' | ||
import * as z from 'zod' | ||
|
||
const ChangePasswordSchema = z | ||
.object({ | ||
old_password: z.string().min(1, "L'ancien mot de passe est requis"), | ||
new_password: z | ||
.string() | ||
.min(8, 'Le mot de passe doit contenir au moins 8 caractères') | ||
.regex(/[A-Z]/, 'Le mot de passe doit contenir au moins une lettre majuscule') | ||
.regex(/[a-z]/, 'Le mot de passe doit contenir au moins une lettre minuscule') | ||
.regex(/[!@#$%^&*()\-=+{}\|:;\"'<>,.?[\]\/\\]/, 'Le mot de passe doit contenir au moins un charactère spécial'), | ||
new_password_confirmation: z.string().min(1, 'La confirmation du mot de passe est requise'), | ||
}) | ||
.superRefine((data, ctx) => { | ||
if (data.new_password !== data.new_password_confirmation) { | ||
return ctx.addIssue({ | ||
path: ['new_password_confirmation'], | ||
code: z.ZodIssueCode.custom, | ||
message: 'Les mots de passe ne correspondent pas', | ||
}) | ||
} | ||
}) | ||
|
||
export default function ChangePasswordScreen() { | ||
const media = useMedia() | ||
const scrollViewContainerStyle = useMemo( | ||
() => ({ | ||
pt: media.gtSm ? '$8' : undefined, | ||
pl: media.gtSm ? '$8' : undefined, | ||
pr: media.gtSm ? '$8' : undefined, | ||
pb: isWeb ? '$10' : '$12', | ||
}), | ||
[media], | ||
) | ||
const { control, formState, handleSubmit, reset, setError } = useForm({ | ||
resolver: zodResolver(ChangePasswordSchema), | ||
mode: 'all', | ||
defaultValues: { | ||
old_password: '', | ||
new_password: '', | ||
new_password_confirmation: '', | ||
}, | ||
}) | ||
const { isDirty, isValid } = formState | ||
const { isPending, mutateAsync } = usetPostChangePassword() | ||
|
||
const onSubmit = handleSubmit((data) => { | ||
mutateAsync(data) | ||
.then(() => { | ||
reset() | ||
}) | ||
.catch((e) => { | ||
if (e instanceof ProfilChangePasswordFormError) { | ||
e.violations.forEach((violation) => { | ||
setError(violation.propertyPath, { message: violation.message }) | ||
}) | ||
} | ||
}) | ||
}) | ||
|
||
return ( | ||
<PageLayout.MainSingleColumn position="relative"> | ||
<KeyboardAvoidingView behavior={Platform.OS === 'android' ? 'height' : 'padding'} style={{ flex: 1 }} keyboardVerticalOffset={100}> | ||
<ScrollView contentContainerStyle={scrollViewContainerStyle}> | ||
<VoxCard> | ||
<VoxCard.Content> | ||
<VoxCard.Title>Modifier mon mot de passe</VoxCard.Title> | ||
<Text.P> Vous devez renseigner votre mot de passe actuel pour changer de mot de passe.</Text.P> | ||
<Controller | ||
control={control} | ||
name="old_password" | ||
render={({ field, fieldState }) => ( | ||
<Input | ||
color="gray" | ||
onChange={field.onChange} | ||
onBlur={field.onBlur} | ||
value={field.value} | ||
placeholder="Mot de passe actuel" | ||
type="password" | ||
error={fieldState.error?.message} | ||
/> | ||
)} | ||
/> | ||
|
||
<Controller | ||
control={control} | ||
name="new_password" | ||
render={({ field, fieldState }) => ( | ||
<Input | ||
color="gray" | ||
onChange={field.onChange} | ||
onBlur={field.onBlur} | ||
value={field.value} | ||
type="password" | ||
placeholder="Nouveau mot de passe" | ||
error={fieldState.error?.message} | ||
/> | ||
)} | ||
/> | ||
|
||
<Controller | ||
control={control} | ||
name="new_password_confirmation" | ||
render={({ field, fieldState }) => ( | ||
<Input | ||
color="gray" | ||
onChange={field.onChange} | ||
onBlur={field.onBlur} | ||
value={field.value} | ||
placeholder="Confirmer le nouveau mot de passe" | ||
type="password" | ||
error={fieldState.error?.message} | ||
/> | ||
)} | ||
/> | ||
<YStack> | ||
<Text.P>Afin de garantir un minimum de sécurité sur les accès votre mot de passe doit contenir au moins :</Text.P> | ||
<YStack pl={12}> | ||
<XStack gap={6}> | ||
<Text.P>•</Text.P> | ||
<Text.P>8 caractères minimum</Text.P> | ||
</XStack> | ||
<XStack gap={6}> | ||
<Text.P>•</Text.P> | ||
<Text.P>Une lettre majuscule</Text.P> | ||
</XStack> | ||
<XStack gap={6}> | ||
<Text.P>•</Text.P> | ||
<Text.P>Une lettre minuscule</Text.P> | ||
</XStack> | ||
<XStack gap={6}> | ||
<Text.P>•</Text.P> | ||
<Text.P>Un caractère spécial</Text.P> | ||
</XStack> | ||
</YStack> | ||
</YStack> | ||
|
||
<XStack justifyContent="flex-end" gap="$2"> | ||
<VoxButton variant="outlined" display={isDirty ? 'flex' : 'none'} onPress={() => reset()}> | ||
Annuler | ||
</VoxButton> | ||
<VoxButton variant="outlined" theme="blue" onPress={onSubmit} loading={isPending} disabled={!isDirty || !isValid}> | ||
Enregister | ||
</VoxButton> | ||
</XStack> | ||
</VoxCard.Content> | ||
</VoxCard> | ||
</ScrollView> | ||
</KeyboardAvoidingView> | ||
</PageLayout.MainSingleColumn> | ||
) | ||
} |
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,39 +1,43 @@ | ||
import { z } from 'zod' | ||
import { createFormErrorResponseSchema, createFormErrorThrower } from '../common/errors/form-errors' | ||
import { propertyPathDeclarationPaymentSchema, propertyPathElectPaymentSchema, propertyPathSchema } from './schema' | ||
import { propertyPathChangePasswordSchema, propertyPathDeclarationPaymentSchema, propertyPathElectPaymentSchema, propertyPathSchema } from './schema' | ||
|
||
const profileFormErrorSchema = createFormErrorResponseSchema(propertyPathSchema) | ||
|
||
export class ProfileFormError extends Error { | ||
violations: z.infer<typeof profileFormErrorSchema>['violations'] | ||
constructor(public errors: z.infer<typeof profileFormErrorSchema>) { | ||
super('FormError') | ||
this.violations = errors.violations | ||
} | ||
} | ||
|
||
export const profileFormErrorThrower = createFormErrorThrower(ProfileFormError, profileFormErrorSchema) | ||
|
||
const profileElectPaymentFormErrorSchema = createFormErrorResponseSchema(propertyPathElectPaymentSchema) | ||
|
||
export class profileElectPaymentFormError extends Error { | ||
violations: z.infer<typeof profileElectPaymentFormErrorSchema>['violations'] | ||
constructor(public errors: z.infer<typeof profileElectPaymentFormErrorSchema>) { | ||
super('FormError') | ||
this.violations = errors.violations | ||
} | ||
} | ||
|
||
export const profileElectPaymentFormErrorThrower = createFormErrorThrower(profileElectPaymentFormError, profileElectPaymentFormErrorSchema) | ||
|
||
const profileElectDeclarationFormErrorSchema = createFormErrorResponseSchema(propertyPathDeclarationPaymentSchema) | ||
|
||
export class profileElectDeclarationFormError extends Error { | ||
violations: z.infer<typeof profileElectDeclarationFormErrorSchema>['violations'] | ||
constructor(public errors: z.infer<typeof profileElectDeclarationFormErrorSchema>) { | ||
super('FormError') | ||
this.violations = errors.violations | ||
} | ||
} | ||
|
||
export const profileElectDeclarationFormErrorThrower = createFormErrorThrower(profileElectDeclarationFormError, profileElectDeclarationFormErrorSchema) | ||
|
||
const profilChangePasswordFormErrorSchema = createFormErrorResponseSchema(propertyPathChangePasswordSchema) | ||
export class ProfilChangePasswordFormError extends Error { | ||
violations: z.infer<typeof profilChangePasswordFormErrorSchema>['violations'] | ||
constructor(public errors: z.infer<typeof profilChangePasswordFormErrorSchema>) { | ||
super('FormError') | ||
this.violations = errors.violations | ||
} | ||
} | ||
export const profilChangePasswordFormErrorThrower = createFormErrorThrower(ProfilChangePasswordFormError, profilChangePasswordFormErrorSchema) |
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