feat: tempcommit

This commit is contained in:
Maximilian Liebmann 2025-06-30 13:59:08 +02:00
parent b191090f90
commit f508f26531
2 changed files with 74 additions and 24 deletions

View file

@ -11,6 +11,7 @@ export default function LabeledInput({
subtext,
placeholder,
value,
defaultValue,
name,
icon,
variantSize = 'default',
@ -31,7 +32,7 @@ export default function LabeledInput({
error?: string;
} & React.InputHTMLAttributes<HTMLInputElement>) {
const [passwordVisible, setPasswordVisible] = React.useState(false);
const [inputValue, setInputValue] = React.useState(value || '');
const [inputValue, setInputValue] = React.useState(value || defaultValue || '');
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);

View file

@ -22,7 +22,11 @@ import {
} from '@/components/ui/select';
import { SettingsDropdown } from '@/components/misc/settings-dropdown';
import { useRouter } from 'next/navigation';
import { useDeleteApiUserMe, useGetApiUserMe } from '@/generated/api/user/user';
import {
useDeleteApiUserMe,
useGetApiUserMe,
usePatchApiUserMePassword,
} from '@/generated/api/user/user';
import { ThemePicker } from './theme-picker';
import LabeledInput from '../custom-ui/labeled-input';
import { GroupWrapper } from '../wrappers/group-wrapper';
@ -55,17 +59,55 @@ import {
DialogTitle,
DialogTrigger,
} from '../ui/dialog';
import useZodForm from '@/lib/hooks/useZodForm';
import { updateUserPasswordServerSchema } from '@/app/api/user/me/validation';
export default function SettingsPage() {
const router = useRouter();
const [currentSection, setCurrentSection] = useState('general');
const { data } = useGetApiUserMe();
const deleteUser = useDeleteApiUserMe();
const updatePassword = usePatchApiUserMePassword();
// Move password state hooks here
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [repeatPassword, setRepeatPassword] = useState('');
const { handleSubmit, formState, register, setError } = useZodForm(
updateUserPasswordServerSchema,
);
const onSubmit = handleSubmit(async (data) => {
await updatePassword.mutateAsync(
{
data: data,
},
{
onSuccess: () => {
router.refresh();
},
onError: (error) => {
if (error instanceof Error) {
setError('root', {
message: error.response?.data.message,
});
} else {
setError('root', {
message: 'An unknown error occurred.',
});
}
},
},
);
});
if (!data) {
return (
<div className='fixed inset-0 flex items-center justify-center p-4 bg-background/50 backdrop-blur-sm'>
<div className='rounded-lg border bg-card text-card-foreground shadow-xl max-w-[700px] w-full h-auto max-h-[calc(100vh-2rem)] flex flex-col'>
<div className='p-6 border-b'>
<h1 className='text-2xl font-semibold'>Loading Settings...</h1>
</div>
</div>
</div>
);
}
const renderSettingsContent = () => {
switch (currentSection) {
@ -85,7 +127,7 @@ export default function SettingsPage() {
type='text'
label='First Name'
placeholder='First Name'
defaultValue={data?.data.user.first_name ?? ''}
defaultValue={data.data.user.first_name ?? ''}
></LabeledInput>
</div>
<div>
@ -93,7 +135,7 @@ export default function SettingsPage() {
type='text'
label='Last Name'
placeholder='Last Name'
defaultValue={data?.data.user.last_name ?? ''}
defaultValue={data.data.user.last_name ?? ''}
></LabeledInput>
</div>
<div className='space-y-2'>
@ -102,7 +144,7 @@ export default function SettingsPage() {
label='Display Name'
icon={UserPen}
placeholder='Display Name'
defaultValue={data?.data.user.name}
defaultValue={data.data.user.name}
></LabeledInput>
</div>
<div className='space-y-2 space-b-2'>
@ -111,7 +153,7 @@ export default function SettingsPage() {
label='Email Address'
icon={MailOpen}
placeholder='Your E-Mail'
defaultValue={data?.data.user.email ?? ''}
defaultValue={data.data.user.email ?? ''}
></LabeledInput>
<span className='text-sm text-muted-foreground'>
@ -123,16 +165,18 @@ export default function SettingsPage() {
{/*-------------------- General Settings --------------------*/}
{/*-------------------- Reset Password --------------------*/}
<GroupWrapper title='Reset Password'>
<div className='flex flex-col sm:flex-row items-center gap-6'>
<div className='flex flex-col sm:flex-row gap-6 w-full'>
{/* Use the state variables directly */}
<div className='flex flex-col items-center gap-6'>
<form
onSubmit={onSubmit}
className='flex flex-col sm:flex-row gap-6 w-full'
>
<div className='flex-1'>
<LabeledInput
type='password'
label='Current Password'
icon={FileKey}
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
{...register('current_password')}
error={formState.errors.current_password?.message}
/>
</div>
<div className='flex-1'>
@ -140,8 +184,8 @@ export default function SettingsPage() {
type='password'
label='New Password'
icon={FileKey2}
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
{...register('new_password')}
error={formState.errors.new_password?.message}
/>
</div>
<div className='flex-1'>
@ -149,34 +193,39 @@ export default function SettingsPage() {
type='password'
label='Repeat Password'
icon={RotateCcwKey}
value={repeatPassword}
onChange={(e) => setRepeatPassword(e.target.value)}
{...register('confirm_new_password')}
error={formState.errors.confirm_new_password?.message}
/>
</div>
<div className='flex items-end'>
<Button
variant={
currentPassword || newPassword || repeatPassword
formState.isValid
? 'outline_secondary'
: 'outline_muted'
}
size='icon'
className='w-full md:size-9'
disabled={
!(currentPassword || newPassword || repeatPassword)
!formState.isValid || formState.isSubmitting
}
>
<BookKey className='h-[1.2rem] w-[1.2rem]' />
</Button>
</div>
</div>
</form>
{formState.errors.root && (
<p className='text-red-500 text-sm mt-1'>
{formState.errors.root.message}
</p>
)}
</div>
</GroupWrapper>
{/*-------------------- Reset Password --------------------*/}
{/*-------------------- Profile Picture --------------------*/}
<GroupWrapper title='Profile Picture'>
<div className='space-y-2 grid grid-cols-[1fr_auto]'>
<ProfilePictureUpload className='file:border file:rounded-md file:hover:bg-disabled-destructive' />
<ProfilePictureUpload />
</div>
</GroupWrapper>
{/*-------------------- Profile Picture --------------------*/}
@ -216,7 +265,7 @@ export default function SettingsPage() {
>
<div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'>
<Dialog>
<DialogTrigger>
<DialogTrigger asChild>
<Button variant='destructive'>Delete Account</Button>
</DialogTrigger>
<DialogContent>