Enable RLS on rate limits table

This commit is contained in:
gpt-engineer-app[bot]
2025-10-14 19:24:54 +00:00
parent fd17234b67
commit 0a325d7c37
11 changed files with 821 additions and 252 deletions

58
src/hooks/useAutoSave.ts Normal file
View File

@@ -0,0 +1,58 @@
import { useEffect, useRef, useState } from 'react';
import { useDebounce } from './useDebounce';
export type AutoSaveOptions<T> = {
data: T;
onSave: (data: T) => Promise<void>;
debounceMs?: number;
enabled?: boolean;
isValid?: boolean;
};
export const useAutoSave = <T,>({
data,
onSave,
debounceMs = 3000,
enabled = true,
isValid = true
}: AutoSaveOptions<T>) => {
const [isSaving, setIsSaving] = useState(false);
const [lastSaved, setLastSaved] = useState<Date | null>(null);
const [error, setError] = useState<string | null>(null);
const debouncedData = useDebounce(data, debounceMs);
const initialRender = useRef(true);
useEffect(() => {
// Skip initial render
if (initialRender.current) {
initialRender.current = false;
return;
}
// Skip if disabled or invalid
if (!enabled || !isValid) return;
const save = async () => {
setIsSaving(true);
setError(null);
try {
await onSave(debouncedData);
setLastSaved(new Date());
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to auto-save');
} finally {
setIsSaving(false);
}
};
save();
}, [debouncedData, enabled, isValid, onSave]);
return {
isSaving,
lastSaved,
error,
resetError: () => setError(null)
};
};

View File

@@ -0,0 +1,84 @@
import { useState, useCallback } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { handleError, handleSuccess } from '@/lib/errorHandler';
export type AvatarUploadState = {
url: string;
imageId: string;
isUploading: boolean;
};
export const useAvatarUpload = (
initialUrl: string = '',
initialImageId: string = '',
username: string
) => {
const [state, setState] = useState<AvatarUploadState>({
url: initialUrl,
imageId: initialImageId,
isUploading: false
});
const uploadAvatar = useCallback(async (
urls: string[],
imageId?: string
) => {
if (!urls[0]) return { success: false };
const newUrl = urls[0];
const newImageId = imageId || '';
// Optimistic update
setState(prev => ({
...prev,
url: newUrl,
imageId: newImageId,
isUploading: true
}));
try {
const { error } = await supabase.rpc('update_profile', {
p_username: username,
p_avatar_url: newUrl,
p_avatar_image_id: newImageId
});
if (error) throw error;
setState(prev => ({ ...prev, isUploading: false }));
handleSuccess('Avatar updated', 'Your avatar has been successfully updated.');
return { success: true };
} catch (error) {
// Rollback on error
setState({
url: initialUrl,
imageId: initialImageId,
isUploading: false
});
handleError(error, {
action: 'Avatar upload failed',
metadata: { username }
});
return { success: false, error };
}
}, [username, initialUrl, initialImageId]);
const resetAvatar = useCallback(() => {
setState({
url: initialUrl,
imageId: initialImageId,
isUploading: false
});
}, [initialUrl, initialImageId]);
return {
avatarUrl: state.url,
avatarImageId: state.imageId,
isUploading: state.isUploading,
uploadAvatar,
resetAvatar
};
};