feat: Implement complete API optimization plan

This commit is contained in:
gpt-engineer-app[bot]
2025-10-31 12:28:24 +00:00
parent 631ce9c89e
commit ca9aa757ae
9 changed files with 363 additions and 333 deletions

View File

@@ -28,6 +28,7 @@ import { handleError, handleSuccess, AppError } from '@/lib/errorHandler';
import { useAvatarUpload } from '@/hooks/useAvatarUpload';
import { useUsernameValidation } from '@/hooks/useUsernameValidation';
import { useAutoSave } from '@/hooks/useAutoSave';
import { useProfileUpdateMutation } from '@/hooks/profile/useProfileUpdateMutation';
import { formatDistanceToNow } from 'date-fns';
import { cn } from '@/lib/utils';
@@ -42,7 +43,7 @@ type ProfileFormData = z.infer<typeof profileSchema>;
export function AccountProfileTab() {
const { user, pendingEmail, clearPendingEmail } = useAuth();
const { data: profile, refreshProfile } = useProfile(user?.id);
const [loading, setLoading] = useState(false);
const updateProfileMutation = useProfileUpdateMutation();
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [showEmailDialog, setShowEmailDialog] = useState(false);
const [showCancelEmailDialog, setShowCancelEmailDialog] = useState(false);
@@ -107,47 +108,28 @@ export function AccountProfileTab() {
const handleFormSubmit = async (data: ProfileFormData) => {
if (!user) return;
setLoading(true);
try {
// Use the update_profile RPC function with server-side validation
const { data: result, error } = await supabase.rpc('update_profile', {
p_username: data.username,
p_display_name: data.display_name || null,
p_bio: data.bio || null
});
if (error) {
// Handle rate limiting error
if (error.code === 'P0001') {
const resetTime = error.message.match(/Try again at (.+)$/)?.[1];
throw new AppError(
error.message,
'RATE_LIMIT',
`Too many profile updates. ${resetTime ? 'Try again at ' + new Date(resetTime).toLocaleTimeString() : 'Please wait a few minutes.'}`
);
// Update Novu subscriber if username changed (before mutation for optimistic update)
const usernameChanged = data.username !== profile?.username;
updateProfileMutation.mutate({
userId: user.id,
updates: {
username: data.username,
display_name: data.display_name || null,
bio: data.bio || null
}
}, {
onSuccess: async () => {
if (usernameChanged && notificationService.isEnabled()) {
await notificationService.updateSubscriber({
subscriberId: user.id,
email: user.email,
firstName: data.username,
});
}
throw error;
await refreshProfile();
}
// Type the RPC result
const rpcResult = result as unknown as { success: boolean; changes_count: number };
// Update Novu subscriber if username changed
if (rpcResult?.changes_count > 0 && notificationService.isEnabled()) {
await notificationService.updateSubscriber({
subscriberId: user.id,
email: user.email,
firstName: data.username,
});
}
await refreshProfile();
handleSuccess('Profile updated', 'Your profile has been successfully updated.');
} catch (error: unknown) {
handleError(error, { action: 'Update profile', userId: user.id });
} finally {
setLoading(false);
}
});
};
const onSubmit = async (data: ProfileFormData) => {
@@ -400,17 +382,17 @@ export function AccountProfileTab() {
<Button
type="submit"
disabled={
loading ||
updateProfileMutation.isPending ||
isDeactivated ||
isSaving ||
usernameValidation.isChecking ||
usernameValidation.isAvailable === false
}
>
{loading || isSaving ? 'Saving...' : 'Save Changes'}
{updateProfileMutation.isPending || isSaving ? 'Saving...' : 'Save Changes'}
</Button>
{lastSaved && !loading && !isSaving && (
{lastSaved && !updateProfileMutation.isPending && !isSaving && (
<span className="text-sm text-muted-foreground">
Last saved {formatDistanceToNow(lastSaved, { addSuffix: true })}
</span>