Compare commits
No commits in common. "d9868dd49f055a24de10a56a6d4581ab1b319200" and "4ce7d2ec07c280a53999ca11c5ab366b6fc8bfee" have entirely different histories.
d9868dd49f
...
4ce7d2ec07
@ -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 && (
|
||||||
|
|||||||
@ -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);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user