feat: Initialize ComFi project with Vite

Setup project structure, dependencies, and basic configuration for the ComFi application. Includes initial setup for Vite, React, TypeScript, Tailwind CSS, and essential development tools. Defines core types and provides a basic README for local development.
This commit is contained in:
MMrp89
2026-02-09 20:28:37 -03:00
parent 1e6a56d866
commit 1a57ac7754
28 changed files with 6070 additions and 8 deletions

82
contexts/ToastContext.tsx Normal file
View File

@@ -0,0 +1,82 @@
import React, { createContext, useContext, useState, useCallback } from 'react';
import { Toast } from '../types';
import { X, CheckCircle2, AlertCircle, Info, AlertTriangle } from 'lucide-react';
interface ToastContextData {
addToast: (toast: Omit<Toast, 'id'>) => void;
removeToast: (id: string) => void;
}
const ToastContext = createContext<ToastContextData>({} as ToastContextData);
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [toasts, setToasts] = useState<Toast[]>([]);
const addToast = useCallback(({ type, title, message, duration = 4000 }: Omit<Toast, 'id'>) => {
const id = Math.random().toString(36).substring(2, 9);
const newToast = { id, type, title, message, duration };
setToasts((state) => [...state, newToast]);
if (duration > 0) {
setTimeout(() => {
removeToast(id);
}, duration);
}
}, []);
const removeToast = useCallback((id: string) => {
setToasts((state) => state.filter((toast) => toast.id !== id));
}, []);
return (
<ToastContext.Provider value={{ addToast, removeToast }}>
{children}
{/* Toast Container Rendered Here Globally */}
<div className="fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none">
{toasts.map((toast) => (
<div
key={toast.id}
className={`pointer-events-auto min-w-[300px] max-w-sm rounded-xl p-4 shadow-xl border flex items-start gap-3 animate-slide-in-right transform transition-all duration-300 ${
toast.type === 'success' ? 'bg-white border-green-100' :
toast.type === 'error' ? 'bg-white border-red-100' :
toast.type === 'warning' ? 'bg-white border-amber-100' :
'bg-white border-blue-100'
}`}
>
<div className={`mt-0.5 ${
toast.type === 'success' ? 'text-green-500' :
toast.type === 'error' ? 'text-red-500' :
toast.type === 'warning' ? 'text-amber-500' :
'text-blue-500'
}`}>
{toast.type === 'success' && <CheckCircle2 size={20} />}
{toast.type === 'error' && <AlertCircle size={20} />}
{toast.type === 'warning' && <AlertTriangle size={20} />}
{toast.type === 'info' && <Info size={20} />}
</div>
<div className="flex-1">
<h4 className={`text-sm font-bold ${
toast.type === 'success' ? 'text-green-800' :
toast.type === 'error' ? 'text-red-800' :
toast.type === 'warning' ? 'text-amber-800' :
'text-blue-800'
}`}>{toast.title}</h4>
{toast.message && <p className="text-xs text-slate-500 mt-1 leading-relaxed">{toast.message}</p>}
</div>
<button onClick={() => removeToast(toast.id)} className="text-slate-400 hover:text-slate-600">
<X size={16} />
</button>
</div>
))}
</div>
</ToastContext.Provider>
);
};
export const useToast = () => {
const context = useContext(ToastContext);
if (!context) throw new Error('useToast must be used within a ToastProvider');
return context;
};