diff --git a/client/src/components/profile/EditProfileDialog.tsx b/client/src/components/profile/EditProfileDialog.tsx new file mode 100644 index 0000000..cd3988b --- /dev/null +++ b/client/src/components/profile/EditProfileDialog.tsx @@ -0,0 +1,198 @@ +import { useState, type ReactNode, type SubmitEvent } from 'react'; +import * as RadixDialog from '@radix-ui/react-dialog'; +import { useMutation } from '@apollo/client/react'; +import { Button } from '../ui/Button'; +import { Field } from '../ui/Field'; +import { Input } from '../ui/Input'; +import { useCurrentUser } from '../../lib/useCurrentUser'; +import { CHANGE_PASSWORD, UPDATE_PROFILE } from '../../graphql/auth'; + +// passordet må være minst så mange tegn +const MIN_PASSWORD_LENGTH = 6; + +type EditProfileDialogProps = { + trigger: ReactNode; +}; + +// popup der brukeren kan endre navn og passord +export function EditProfileDialog({ trigger }: EditProfileDialogProps) { + const { user } = useCurrentUser(); + + const [name, setName] = useState(user?.name ?? ''); + const [nameMessage, setNameMessage] = useState(''); + + const [currentPassword, setCurrentPassword] = useState(''); + const [newPassword, setNewPassword] = useState(''); + const [repeatPassword, setRepeatPassword] = useState(''); + const [passwordMessage, setPasswordMessage] = useState(''); + + const [updateProfile, { loading: savingName }] = useMutation(UPDATE_PROFILE); + const [changePassword, { loading: savingPassword }] = + useMutation(CHANGE_PASSWORD); + + // tømmer feltene når popupen lukkes + function handleOpenChange(open: boolean) { + if (!open) { + setName(user?.name ?? ''); + setNameMessage(''); + setCurrentPassword(''); + setNewPassword(''); + setRepeatPassword(''); + setPasswordMessage(''); + } + } + + async function saveName(event: SubmitEvent) { + // hindrer at siden lastes på nytt + event.preventDefault(); + + if (!name.trim()) { + setNameMessage('Navnet kan ikke være tomt.'); + return; + } + + try { + await updateProfile({ variables: { name: name.trim() } }); + setNameMessage('Navnet er lagret.'); + } catch (error) { + setNameMessage( + error instanceof Error ? error.message : 'Kunne ikke lagre navnet.', + ); + } + } + + async function savePassword(event: SubmitEvent) { + event.preventDefault(); + + if (!currentPassword || !newPassword || !repeatPassword) { + setPasswordMessage('Fyll inn alle feltene.'); + return; + } + + if (newPassword.length < MIN_PASSWORD_LENGTH) { + setPasswordMessage( + `Nytt passord må være minst ${MIN_PASSWORD_LENGTH} tegn.`, + ); + return; + } + + if (newPassword !== repeatPassword) { + setPasswordMessage('Passordene er ikke like.'); + return; + } + + try { + await changePassword({ variables: { currentPassword, newPassword } }); + setCurrentPassword(''); + setNewPassword(''); + setRepeatPassword(''); + setPasswordMessage('Passordet er byttet.'); + } catch (error) { + setPasswordMessage( + error instanceof Error ? error.message : 'Kunne ikke bytte passord.', + ); + } + } + + return ( + + {trigger} + + + {/* popup som kan scrolles på små skjermer */} + + + Rediger profil + + + Endre navnet ditt eller bytt passord. + + + {/* navn */} +
+ + setName(event.target.value)} + maxLength={50} + autoComplete="name" + /> + + {nameMessage && ( +

+ {nameMessage} +

+ )} + +
+ + {/* passord */} +
+

Bytt passord

+ + setCurrentPassword(event.target.value)} + autoComplete="current-password" + /> + + + setNewPassword(event.target.value)} + placeholder={`Minst ${MIN_PASSWORD_LENGTH} tegn`} + autoComplete="new-password" + /> + + + setRepeatPassword(event.target.value)} + autoComplete="new-password" + /> + + {passwordMessage && ( +

+ {passwordMessage} +

+ )} + +
+ + + + +
+
+
+ ); +} diff --git a/client/src/graphql/auth.ts b/client/src/graphql/auth.ts index abdd6fb..c5d08a5 100644 --- a/client/src/graphql/auth.ts +++ b/client/src/graphql/auth.ts @@ -64,7 +64,6 @@ export const CONTINUE_AS_GUEST = gql` ${USER_FIELDS} `; - // lagrer språk, måleenheter og allergier export const UPDATE_PREFERENCES: TypedDocumentNode< { updatePreferences: BackendUser }, @@ -85,3 +84,26 @@ export const UPDATE_PREFERENCES: TypedDocumentNode< } ${USER_FIELDS} `; + +// endrer navnet som vises på profilen +export const UPDATE_PROFILE: TypedDocumentNode< + { updateProfile: BackendUser }, + { name: string } +> = gql` + mutation UpdateProfile($name: String!) { + updateProfile(name: $name) { + ...UserFields + } + } + ${USER_FIELDS} +`; + +// bytter passord hvis det gamle stemmer +export const CHANGE_PASSWORD: TypedDocumentNode< + { changePassword: boolean }, + { currentPassword: string; newPassword: string } +> = gql` + mutation ChangePassword($currentPassword: String!, $newPassword: String!) { + changePassword(currentPassword: $currentPassword, newPassword: $newPassword) + } +`; diff --git a/client/src/routes/ProfilePage.tsx b/client/src/routes/ProfilePage.tsx index 6e3f9ab..4ad12cf 100644 --- a/client/src/routes/ProfilePage.tsx +++ b/client/src/routes/ProfilePage.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { Link, useNavigate } from 'react-router'; import { Button } from '../components/ui/Button'; import { PreferencesForm } from '../components/profile/PreferencesForm'; +import { EditProfileDialog } from '../components/profile/EditProfileDialog'; import { useCurrentUser } from '../lib/useCurrentUser'; import { useQuery } from '@apollo/client/react'; import { MY_RECIPES } from '../graphql/profile'; @@ -118,13 +119,16 @@ export default function ProfilePage() {
- + + Rediger profil + + } + />