refactor: streamline toast system and harmonize UI across tools

- Migrate all toast notifications to sonner and remove custom ToastProvider
- Align Card and TextInput styling across Figlet and Pastel (rounded-lg, border-based)
- Fix build error by removing non-existent export in lib/units/index.ts
- Clean up unused Figlet components and constants
This commit is contained in:
2026-02-23 02:04:46 +01:00
parent 09838a203c
commit a9d0fd8443
25 changed files with 109 additions and 808 deletions

View File

@@ -19,7 +19,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
{
'bg-primary text-primary-foreground shadow-[0_0_20px_rgba(139,92,246,0.3)] hover:bg-primary/90 hover:shadow-[0_0_25px_rgba(139,92,246,0.5)] hover:-translate-y-0.5':
variant === 'default',
'glass bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 text-foreground':
'glass hover:bg-accent/10 hover:border-primary/20 text-foreground':
variant === 'outline',
'hover:bg-accent hover:text-accent-foreground': variant === 'ghost',
'bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-lg hover:shadow-destructive/20':

View File

@@ -5,7 +5,7 @@ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElemen
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('glass rounded-2xl text-card-foreground shadow-xl transition-all duration-300', className)}
className={cn('bg-card text-card-foreground border rounded-lg transition-all duration-300', className)}
{...props}
/>
)

View File

@@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
'flex h-10 w-full rounded-xl border border-white/10 bg-white/5 px-4 py-2',
'flex h-10 w-full rounded-xl border border-border bg-input px-4 py-2',
'text-sm ring-offset-background file:border-0 file:bg-transparent',
'file:text-sm file:font-medium placeholder:text-muted-foreground',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:border-primary/50',

View File

@@ -18,7 +18,7 @@ const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
)}
<select
className={cn(
'flex h-10 w-full rounded-xl border border-white/10 bg-white/5 px-3 py-2',
'flex h-10 w-full rounded-xl border border-border bg-input px-3 py-2',
'text-sm ring-offset-background',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:border-primary/50',
'disabled:cursor-not-allowed disabled:opacity-50 transition-all duration-200',

View File

@@ -1,90 +0,0 @@
'use client';
import * as React from 'react';
import { X, CheckCircle2, AlertCircle, Info } from 'lucide-react';
import { cn } from '@/lib/utils/cn';
export type ToastType = 'success' | 'error' | 'info';
export interface Toast {
id: string;
message: string;
type: ToastType;
}
interface ToastContextType {
toasts: Toast[];
addToast: (message: string, type?: ToastType) => void;
removeToast: (id: string) => void;
}
const ToastContext = React.createContext<ToastContextType | undefined>(undefined);
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = React.useState<Toast[]>([]);
const addToast = React.useCallback((message: string, type: ToastType = 'success') => {
const id = Math.random().toString(36).substring(7);
setToasts((prev) => [...prev, { id, message, type }]);
// Auto remove after 3 seconds
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, 3000);
}, []);
const removeToast = React.useCallback((id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, []);
return (
<ToastContext.Provider value={{ toasts, addToast, removeToast }}>
{children}
<div className="fixed bottom-4 right-4 z-50 flex flex-col gap-2 pointer-events-none">
{toasts.map((toast) => (
<ToastItem key={toast.id} toast={toast} onClose={() => removeToast(toast.id)} />
))}
</div>
</ToastContext.Provider>
);
}
function ToastItem({ toast, onClose }: { toast: Toast; onClose: () => void }) {
const Icon = toast.type === 'success' ? CheckCircle2 : toast.type === 'error' ? AlertCircle : Info;
return (
<div
className={cn(
'flex items-center gap-3 px-4 py-3 rounded-lg shadow-lg pointer-events-auto',
'animate-in slide-in-from-right-full duration-300',
'min-w-[300px] max-w-[400px]',
{
'bg-green-50 text-green-900 border border-green-200 dark:bg-green-900/20 dark:text-green-100 dark:border-green-800':
toast.type === 'success',
'bg-red-50 text-red-900 border border-red-200 dark:bg-red-900/20 dark:text-red-100 dark:border-red-800':
toast.type === 'error',
'bg-blue-50 text-blue-900 border border-blue-200 dark:bg-blue-900/20 dark:text-blue-100 dark:border-blue-800':
toast.type === 'info',
}
)}
>
<Icon className="h-5 w-5 flex-shrink-0" />
<p className="text-sm font-medium flex-1">{toast.message}</p>
<button
onClick={onClose}
className="flex-shrink-0 opacity-70 hover:opacity-100 transition-opacity"
aria-label="Close"
>
<X className="h-4 w-4" />
</button>
</div>
);
}
export function useToast() {
const context = React.useContext(ToastContext);
if (!context) {
throw new Error('useToast must be used within ToastProvider');
}
return context;
}