mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 09:51:13 -05:00
Enable RLS on rate limits table
This commit is contained in:
89
src/lib/deletionDialogMachine.ts
Normal file
89
src/lib/deletionDialogMachine.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
export type DeletionStep = 'warning' | 'confirm' | 'code';
|
||||
|
||||
export type DeletionDialogState = {
|
||||
step: DeletionStep;
|
||||
confirmationCode: string;
|
||||
codeReceived: boolean;
|
||||
scheduledDate: string;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
};
|
||||
|
||||
export type DeletionDialogAction =
|
||||
| { type: 'CONTINUE_TO_CONFIRM' }
|
||||
| { type: 'GO_BACK_TO_WARNING' }
|
||||
| { type: 'REQUEST_DELETION'; payload: { scheduledDate: string } }
|
||||
| { type: 'UPDATE_CODE'; payload: { code: string } }
|
||||
| { type: 'TOGGLE_CODE_RECEIVED' }
|
||||
| { type: 'SET_LOADING'; payload: boolean }
|
||||
| { type: 'SET_ERROR'; payload: string | null }
|
||||
| { type: 'RESET' };
|
||||
|
||||
export const initialState: DeletionDialogState = {
|
||||
step: 'warning',
|
||||
confirmationCode: '',
|
||||
codeReceived: false,
|
||||
scheduledDate: '',
|
||||
isLoading: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
export function deletionDialogReducer(
|
||||
state: DeletionDialogState,
|
||||
action: DeletionDialogAction
|
||||
): DeletionDialogState {
|
||||
switch (action.type) {
|
||||
case 'CONTINUE_TO_CONFIRM':
|
||||
return { ...state, step: 'confirm' };
|
||||
|
||||
case 'GO_BACK_TO_WARNING':
|
||||
return { ...state, step: 'warning', error: null };
|
||||
|
||||
case 'REQUEST_DELETION':
|
||||
return {
|
||||
...state,
|
||||
step: 'code',
|
||||
scheduledDate: action.payload.scheduledDate,
|
||||
isLoading: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
case 'UPDATE_CODE':
|
||||
// Only allow digits, max 6
|
||||
const sanitized = action.payload.code.replace(/\D/g, '').slice(0, 6);
|
||||
return { ...state, confirmationCode: sanitized };
|
||||
|
||||
case 'TOGGLE_CODE_RECEIVED':
|
||||
return { ...state, codeReceived: !state.codeReceived };
|
||||
|
||||
case 'SET_LOADING':
|
||||
return { ...state, isLoading: action.payload };
|
||||
|
||||
case 'SET_ERROR':
|
||||
return { ...state, error: action.payload, isLoading: false };
|
||||
|
||||
case 'RESET':
|
||||
return initialState;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation helpers
|
||||
export const canProceedToConfirm = (state: DeletionDialogState): boolean => {
|
||||
return state.step === 'warning' && !state.isLoading;
|
||||
};
|
||||
|
||||
export const canRequestDeletion = (state: DeletionDialogState): boolean => {
|
||||
return state.step === 'confirm' && !state.isLoading;
|
||||
};
|
||||
|
||||
export const canConfirmDeletion = (state: DeletionDialogState): boolean => {
|
||||
return (
|
||||
state.step === 'code' &&
|
||||
state.confirmationCode.length === 6 &&
|
||||
state.codeReceived &&
|
||||
!state.isLoading
|
||||
);
|
||||
};
|
||||
63
src/lib/errorHandler.ts
Normal file
63
src/lib/errorHandler.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { toast } from 'sonner';
|
||||
import { logger } from './logger';
|
||||
|
||||
export type ErrorContext = {
|
||||
action: string;
|
||||
userId?: string;
|
||||
metadata?: Record<string, any>;
|
||||
};
|
||||
|
||||
export class AppError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public code: string,
|
||||
public userMessage?: string
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'AppError';
|
||||
}
|
||||
}
|
||||
|
||||
export const handleError = (
|
||||
error: unknown,
|
||||
context: ErrorContext
|
||||
): void => {
|
||||
const errorMessage = error instanceof AppError
|
||||
? error.userMessage || error.message
|
||||
: error instanceof Error
|
||||
? error.message
|
||||
: 'An unexpected error occurred';
|
||||
|
||||
// Log to console/monitoring
|
||||
logger.error('Error occurred', {
|
||||
...context,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined
|
||||
});
|
||||
|
||||
// Show user-friendly toast
|
||||
toast.error(context.action, {
|
||||
description: errorMessage,
|
||||
duration: 5000
|
||||
});
|
||||
};
|
||||
|
||||
export const handleSuccess = (
|
||||
title: string,
|
||||
description?: string
|
||||
): void => {
|
||||
toast.success(title, {
|
||||
description,
|
||||
duration: 3000
|
||||
});
|
||||
};
|
||||
|
||||
export const handleInfo = (
|
||||
title: string,
|
||||
description?: string
|
||||
): void => {
|
||||
toast.info(title, {
|
||||
description,
|
||||
duration: 4000
|
||||
});
|
||||
};
|
||||
@@ -7,14 +7,24 @@
|
||||
|
||||
const isDev = import.meta.env.DEV;
|
||||
|
||||
type LogContext = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export const logger = {
|
||||
log: (...args: any[]) => {
|
||||
if (isDev) console.log(...args);
|
||||
},
|
||||
error: (...args: any[]) => {
|
||||
console.error(...args); // Always log errors
|
||||
error: (message: string, context?: LogContext) => {
|
||||
console.error(message, context); // Always log errors
|
||||
},
|
||||
warn: (...args: any[]) => {
|
||||
if (isDev) console.warn(...args);
|
||||
},
|
||||
info: (...args: any[]) => {
|
||||
if (isDev) console.info(...args);
|
||||
},
|
||||
debug: (...args: any[]) => {
|
||||
if (isDev) console.debug(...args);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user