Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/supabaseClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,38 @@ import { createClient } from '@supabase/supabase-js';
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);
// Validate environment variables
if (!supabaseUrl || !supabaseAnonKey) {
console.error('Missing Supabase environment variables');
throw new Error(
'Supabase configuration error: VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY must be defined in environment variables'
);
}

// Validate URL format
try {
new URL(supabaseUrl);
} catch (error) {
throw new Error(`Invalid Supabase URL format: ${supabaseUrl}`);
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

// Helper function to handle Supabase errors
export const handleSupabaseError = (error, context = '') => {
console.error(`Supabase error${context ? ` in ${context}` : ''}:`, error);

if (error.message) {
return {
success: false,
error: error.message,
context
};
}

return {
success: false,
error: 'An unexpected error occurred',
context
};
};
88 changes: 88 additions & 0 deletions src/utils/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Centralized error handling utilities
*/

/**
* Error types for categorization
*/
export const ErrorTypes = {
VALIDATION: 'VALIDATION_ERROR',
NETWORK: 'NETWORK_ERROR',
AUTH: 'AUTHENTICATION_ERROR',
DATABASE: 'DATABASE_ERROR',
UNKNOWN: 'UNKNOWN_ERROR'
};

/**
* Custom error class for application errors
*/
export class AppError extends Error {
constructor(message, type = ErrorTypes.UNKNOWN, details = {}) {
super(message);
this.name = 'AppError';
this.type = type;
this.details = details;
this.timestamp = new Date().toISOString();
}
}

/**
* Logs error with context
* @param {Error} error - The error to log
* @param {string} context - Context where error occurred
*/
export const logError = (error, context = '') => {
const errorInfo = {
message: error.message,
type: error.type || ErrorTypes.UNKNOWN,
context,
timestamp: new Date().toISOString(),
stack: error.stack
};

console.error('Application Error:', errorInfo);

// In production, you might want to send this to an error tracking service
// Example: sendToErrorTracker(errorInfo);
};

/**
* Creates a user-friendly error message
* @param {Error} error - The error object
* @returns {string} - User-friendly message
*/
export const getUserFriendlyMessage = (error) => {
if (error instanceof AppError) {
switch (error.type) {
case ErrorTypes.VALIDATION:
return error.message;
case ErrorTypes.NETWORK:
return 'Network error. Please check your connection and try again.';
case ErrorTypes.AUTH:
return 'Authentication failed. Please log in again.';
case ErrorTypes.DATABASE:
return 'Database error. Please try again later.';
default:
return 'An unexpected error occurred. Please try again.';
}
}

return error.message || 'An unexpected error occurred.';
};

/**
* Error boundary wrapper for async operations
* @param {Function} fn - Function to wrap
* @param {string} context - Context for error logging
* @returns {Function} - Wrapped function
*/
export const withErrorBoundary = (fn, context) => {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
logError(error, context);
throw error;
}
};
};
119 changes: 119 additions & 0 deletions src/utils/validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* Utility functions for input validation and error handling
*/

/**
* Validates if a value is not null or undefined
* @param {*} value - The value to validate
* @param {string} fieldName - Name of the field for error messages
* @returns {boolean} - True if valid
* @throws {Error} - If validation fails
*/
export const validateRequired = (value, fieldName = 'Field') => {
if (value === null || value === undefined) {
throw new Error(`${fieldName} is required`);
}
return true;
};

/**
* Validates if a string is not empty
* @param {string} value - The string to validate
* @param {string} fieldName - Name of the field for error messages
* @returns {boolean} - True if valid
* @throws {Error} - If validation fails
*/
export const validateNonEmptyString = (value, fieldName = 'Field') => {
validateRequired(value, fieldName);

if (typeof value !== 'string') {
throw new Error(`${fieldName} must be a string`);
}

if (value.trim().length === 0) {
throw new Error(`${fieldName} cannot be empty`);
}

return true;
};

/**
* Validates email format
* @param {string} email - The email to validate
* @returns {boolean} - True if valid
* @throws {Error} - If validation fails
*/
export const validateEmail = (email) => {
validateNonEmptyString(email, 'Email');

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error('Invalid email format');
}

return true;
};

/**
* Validates if a value is a positive number
* @param {number} value - The number to validate
* @param {string} fieldName - Name of the field for error messages
* @returns {boolean} - True if valid
* @throws {Error} - If validation fails
*/
export const validatePositiveNumber = (value, fieldName = 'Value') => {
validateRequired(value, fieldName);

if (typeof value !== 'number' || isNaN(value)) {
throw new Error(`${fieldName} must be a valid number`);
}

if (value <= 0) {
throw new Error(`${fieldName} must be positive`);
}

return true;
};

/**
* Safely executes an async function with error handling
* @param {Function} asyncFn - The async function to execute
* @param {string} context - Context for error messages
* @returns {Promise<{success: boolean, data?: any, error?: string}>}
*/
export const safeAsync = async (asyncFn, context = 'Operation') => {
try {
const result = await asyncFn();
return {
success: true,
data: result
};
} catch (error) {
console.error(`Error in ${context}:`, error);
return {
success: false,
error: error.message || 'An unexpected error occurred'
};
}
};

/**
* Validates an array is not empty
* @param {Array} arr - The array to validate
* @param {string} fieldName - Name of the field for error messages
* @returns {boolean} - True if valid
* @throws {Error} - If validation fails
*/
export const validateNonEmptyArray = (arr, fieldName = 'Array') => {
validateRequired(arr, fieldName);

if (!Array.isArray(arr)) {
throw new Error(`${fieldName} must be an array`);
}

if (arr.length === 0) {
throw new Error(`${fieldName} cannot be empty`);
}

return true;
};