Compare commits

..

No commits in common. "d9868dd49f055a24de10a56a6d4581ab1b319200" and "4ce7d2ec07c280a53999ca11c5ab366b6fc8bfee" have entirely different histories.

4 changed files with 12 additions and 98 deletions

View File

@ -6,13 +6,13 @@
'use client'; 'use client';
import { useState, useEffect } from 'react'; import { useState } from 'react';
import { useAuth } from '@/lib/context/auth-context'; import { useAuth } from '@/lib/context/auth-context';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod'; import { z } from 'zod';
import { updateUser, changePassword } from '@/lib/api'; import { updateUser } from '@/lib/api';
// Password update schema // Password update schema
const passwordSchema = z const passwordSchema = z
@ -44,7 +44,7 @@ const profileSchema = z.object({
type ProfileFormData = z.infer<typeof profileSchema>; type ProfileFormData = z.infer<typeof profileSchema>;
export default function ProfilePage() { export default function ProfilePage() {
const { user, refreshUser, loading } = useAuth(); const { user, refreshUser } = useAuth();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [activeTab, setActiveTab] = useState<'profile' | 'password'>('profile'); const [activeTab, setActiveTab] = useState<'profile' | 'password'>('profile');
const [successMessage, setSuccessMessage] = useState(''); const [successMessage, setSuccessMessage] = useState('');
@ -63,37 +63,8 @@ export default function ProfilePage() {
// Password form // Password form
const passwordForm = useForm<PasswordFormData>({ const passwordForm = useForm<PasswordFormData>({
resolver: zodResolver(passwordSchema), resolver: zodResolver(passwordSchema),
defaultValues: {
currentPassword: '',
newPassword: '',
confirmPassword: '',
},
}); });
// Update form values when user data loads
useEffect(() => {
if (user) {
profileForm.reset({
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user]);
// Reset password form when switching to password tab
useEffect(() => {
if (activeTab === 'password') {
passwordForm.reset({
currentPassword: '',
newPassword: '',
confirmPassword: '',
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTab]);
// Update profile mutation // Update profile mutation
const updateProfileMutation = useMutation({ const updateProfileMutation = useMutation({
mutationFn: (data: ProfileFormData) => { mutationFn: (data: ProfileFormData) => {
@ -113,22 +84,17 @@ export default function ProfilePage() {
}, },
}); });
// Update password mutation // Update password mutation (you'll need to add this endpoint)
const updatePasswordMutation = useMutation({ const updatePasswordMutation = useMutation({
mutationFn: async (data: PasswordFormData) => { mutationFn: async (data: PasswordFormData) => {
return changePassword({ // TODO: Add password update endpoint
currentPassword: data.currentPassword, // return updatePassword(data);
newPassword: data.newPassword, return Promise.resolve({ success: true });
});
}, },
onSuccess: () => { onSuccess: () => {
setSuccessMessage('Password updated successfully!'); setSuccessMessage('Password updated successfully!');
setErrorMessage(''); setErrorMessage('');
passwordForm.reset({ passwordForm.reset();
currentPassword: '',
newPassword: '',
confirmPassword: '',
});
setTimeout(() => setSuccessMessage(''), 3000); setTimeout(() => setSuccessMessage(''), 3000);
}, },
onError: (error: any) => { onError: (error: any) => {
@ -145,35 +111,6 @@ export default function ProfilePage() {
updatePasswordMutation.mutate(data); updatePasswordMutation.mutate(data);
}; };
// Show loading state while user data is being fetched
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">Loading profile...</p>
</div>
</div>
);
}
// Show error if user is not found after loading
if (!loading && !user) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<p className="text-red-600 mb-4">Unable to load user profile</p>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
Retry
</button>
</div>
</div>
);
}
return ( return (
<div className="max-w-4xl mx-auto space-y-6"> <div className="max-w-4xl mx-auto space-y-6">
{/* Header */} {/* Header */}
@ -351,7 +288,6 @@ export default function ProfilePage() {
{...passwordForm.register('currentPassword')} {...passwordForm.register('currentPassword')}
type="password" type="password"
id="currentPassword" id="currentPassword"
autoComplete="current-password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/> />
{passwordForm.formState.errors.currentPassword && ( {passwordForm.formState.errors.currentPassword && (
@ -373,7 +309,6 @@ export default function ProfilePage() {
{...passwordForm.register('newPassword')} {...passwordForm.register('newPassword')}
type="password" type="password"
id="newPassword" id="newPassword"
autoComplete="new-password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/> />
{passwordForm.formState.errors.newPassword && ( {passwordForm.formState.errors.newPassword && (
@ -399,7 +334,6 @@ export default function ProfilePage() {
{...passwordForm.register('confirmPassword')} {...passwordForm.register('confirmPassword')}
type="password" type="password"
id="confirmPassword" id="confirmPassword"
autoComplete="new-password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/> />
{passwordForm.formState.errors.confirmPassword && ( {passwordForm.formState.errors.confirmPassword && (

View File

@ -103,7 +103,7 @@ export const usersApi = {
/** /**
* Change password * Change password
*/ */
async changePassword(data: ChangePasswordRequest): Promise<{ message: string }> { async changePassword(data: ChangePasswordRequest): Promise<void> {
return apiClient.patch<{ message: string }>('/api/v1/users/me/password', data); return apiClient.post<void>('/api/v1/users/change-password', data);
}, },
}; };

View File

@ -53,16 +53,8 @@ export {
type CsvBookingStatsResponse, type CsvBookingStatsResponse,
} from './bookings'; } from './bookings';
// Users (7 endpoints) // Users (6 endpoints)
export { export { listUsers, getUser, createUser, updateUser, deleteUser, restoreUser } from './users';
listUsers,
getUser,
createUser,
updateUser,
deleteUser,
restoreUser,
changePassword,
} from './users';
// Organizations (4 endpoints) // Organizations (4 endpoints)
export { export {

View File

@ -78,15 +78,3 @@ export async function deleteUser(id: string): Promise<SuccessResponse> {
export async function restoreUser(id: string): Promise<UserResponse> { export async function restoreUser(id: string): Promise<UserResponse> {
return post<UserResponse>(`/api/v1/users/${id}/restore`); return post<UserResponse>(`/api/v1/users/${id}/restore`);
} }
/**
* Change own password
* PATCH /api/v1/users/me/password
* Requires: Authentication
*/
export async function changePassword(data: {
currentPassword: string;
newPassword: string;
}): Promise<{ message: string }> {
return patch<{ message: string }>('/api/v1/users/me/password', data);
}