Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
import { Link } from "react-router";
interface AlertProps {
variant: "success" | "error" | "warning" | "info"; // Alert type
title: string; // Title of the alert
message: string | React.ReactNode; // Message of the alert (supports string with \n or JSX)
showLink?: boolean; // Whether to show the "Learn More" link
linkHref?: string; // Link URL
linkText?: string; // Link text
}
const Alert: React.FC<AlertProps> = ({
variant,
title,
message,
showLink = false,
linkHref = "#",
linkText = "Learn more",
}) => {
// Tailwind classes for each variant - matching notification style from image
const variantClasses = {
success: {
container:
"border-b-2 border-success-500 bg-success-50 dark:border-success-500/30 dark:bg-success-500/15",
icon: "text-success-500",
},
error: {
container:
"border-b-2 border-error-500 bg-error-50 dark:border-error-500/30 dark:bg-error-500/15",
icon: "text-error-500",
},
warning: {
container:
"border-b-2 border-warning-500 bg-warning-50 dark:border-warning-500/30 dark:bg-warning-500/15",
icon: "text-warning-500",
},
info: {
container:
"border-b-2 border-blue-light-500 bg-blue-light-50 dark:border-blue-light-500/30 dark:bg-blue-light-500/15",
icon: "text-blue-light-500",
},
};
// Icon for each variant
const icons = {
success: (
<svg
className="fill-current"
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.70186 12.0001C3.70186 7.41711 7.41711 3.70186 12.0001 3.70186C16.5831 3.70186 20.2984 7.41711 20.2984 12.0001C20.2984 16.5831 16.5831 20.2984 12.0001 20.2984C7.41711 20.2984 3.70186 16.5831 3.70186 12.0001ZM12.0001 1.90186C6.423 1.90186 1.90186 6.423 1.90186 12.0001C1.90186 17.5772 6.423 22.0984 12.0001 22.0984C17.5772 22.0984 22.0984 17.5772 22.0984 12.0001C22.0984 6.423 17.5772 1.90186 12.0001 1.90186ZM15.6197 10.7395C15.9712 10.388 15.9712 9.81819 15.6197 9.46672C15.2683 9.11525 14.6984 9.11525 14.347 9.46672L11.1894 12.6243L9.6533 11.0883C9.30183 10.7368 8.73198 10.7368 8.38051 11.0883C8.02904 11.4397 8.02904 12.0096 8.38051 12.3611L10.553 14.5335C10.7217 14.7023 10.9507 14.7971 11.1894 14.7971C11.428 14.7971 11.657 14.7023 11.8257 14.5335L15.6197 10.7395Z"
/>
</svg>
),
error: (
<svg
className="fill-current"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M20.3499 12.0004C20.3499 16.612 16.6115 20.3504 11.9999 20.3504C7.38832 20.3504 3.6499 16.612 3.6499 12.0004C3.6499 7.38881 7.38833 3.65039 11.9999 3.65039C16.6115 3.65039 20.3499 7.38881 20.3499 12.0004ZM11.9999 22.1504C17.6056 22.1504 22.1499 17.6061 22.1499 12.0004C22.1499 6.3947 17.6056 1.85039 11.9999 1.85039C6.39421 1.85039 1.8499 6.3947 1.8499 12.0004C1.8499 17.6061 6.39421 22.1504 11.9999 22.1504ZM13.0008 16.4753C13.0008 15.923 12.5531 15.4753 12.0008 15.4753L11.9998 15.4753C11.4475 15.4753 10.9998 15.923 10.9998 16.4753C10.9998 17.0276 11.4475 17.4753 11.9998 17.4753L12.0008 17.4753C12.5531 17.4753 13.0008 17.0276 13.0008 16.4753ZM11.9998 6.62898C12.414 6.62898 12.7498 6.96476 12.7498 7.37898L12.7498 13.0555C12.7498 13.4697 12.414 13.8055 11.9998 13.8055C11.5856 13.8055 11.2498 13.4697 11.2498 13.0555L11.2498 7.37898C11.2498 6.96476 11.5856 6.62898 11.9998 6.62898Z"
fill="#F04438"
/>
</svg>
),
warning: (
<svg
className="fill-current"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.6501 12.0001C3.6501 7.38852 7.38852 3.6501 12.0001 3.6501C16.6117 3.6501 20.3501 7.38852 20.3501 12.0001C20.3501 16.6117 16.6117 20.3501 12.0001 20.3501C7.38852 20.3501 3.6501 16.6117 3.6501 12.0001ZM12.0001 1.8501C6.39441 1.8501 1.8501 6.39441 1.8501 12.0001C1.8501 17.6058 6.39441 22.1501 12.0001 22.1501C17.6058 22.1501 22.1501 17.6058 22.1501 12.0001C22.1501 6.39441 17.6058 1.8501 12.0001 1.8501ZM10.9992 7.52517C10.9992 8.07746 11.4469 8.52517 11.9992 8.52517H12.0002C12.5525 8.52517 13.0002 8.07746 13.0002 7.52517C13.0002 6.97289 12.5525 6.52517 12.0002 6.52517H11.9992C11.4469 6.52517 10.9992 6.97289 10.9992 7.52517ZM12.0002 17.3715C11.586 17.3715 11.2502 17.0357 11.2502 16.6215V10.945C11.2502 10.5308 11.586 10.195 12.0002 10.195C12.4144 10.195 12.7502 10.5308 12.7502 10.945V16.6215C12.7502 17.0357 12.4144 17.3715 12.0002 17.3715Z"
fill=""
/>
</svg>
),
info: (
<svg
className="fill-current"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.6501 11.9996C3.6501 7.38803 7.38852 3.64961 12.0001 3.64961C16.6117 3.64961 20.3501 7.38803 20.3501 11.9996C20.3501 16.6112 16.6117 20.3496 12.0001 20.3496C7.38852 20.3496 3.6501 16.6112 3.6501 11.9996ZM12.0001 1.84961C6.39441 1.84961 1.8501 6.39392 1.8501 11.9996C1.8501 17.6053 6.39441 22.1496 12.0001 22.1496C17.6058 22.1496 22.1501 17.6053 22.1501 11.9996C22.1501 6.39392 17.6058 1.84961 12.0001 1.84961ZM10.9992 7.52468C10.9992 8.07697 11.4469 8.52468 11.9992 8.52468H12.0002C12.5525 8.52468 13.0002 8.07697 13.0002 7.52468C13.0002 6.9724 12.5525 6.52468 12.0002 6.52468H11.9992C11.4469 6.52468 10.9992 6.9724 10.9992 7.52468ZM12.0002 17.371C11.586 17.371 11.2502 17.0352 11.2502 16.621V10.9445C11.2502 10.5303 11.586 10.1945 12.0002 10.1945C12.4144 10.1945 12.7502 10.5303 12.7502 10.9445V16.621C12.7502 17.0352 12.4144 17.371 12.0002 17.371Z"
fill=""
/>
</svg>
),
};
return (
<div
className={`rounded-xl p-4 ${variantClasses[variant].container}`}
>
<div className="flex items-start gap-3">
<div className={`-mt-0.5 ${variantClasses[variant].icon}`}>
{icons[variant]}
</div>
<div>
<h4 className="mb-1 text-sm font-semibold text-gray-800 dark:text-white/90">
{title}
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400 whitespace-pre-line">
{message}
</p>
{showLink && (
<Link
to={linkHref}
className="inline-block mt-3 text-sm font-medium text-gray-500 underline dark:text-gray-400"
>
{linkText}
</Link>
)}
</div>
</div>
</div>
);
};
export default Alert;

View File

@@ -0,0 +1,200 @@
import React from 'react';
import { Modal } from '../modal';
import Button from '../button/Button';
export type AlertModalVariant = 'success' | 'info' | 'warning' | 'danger';
interface AlertModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
message: string;
variant?: AlertModalVariant;
buttonText?: string;
// Confirmation mode props
isConfirmation?: boolean;
onConfirm?: () => void;
confirmText?: string;
cancelText?: string;
isLoading?: boolean;
itemsList?: string[]; // For showing items being deleted (max 5)
}
export default function AlertModal({
isOpen,
onClose,
title,
message,
variant = 'info',
buttonText = 'Okay, Got It',
isConfirmation = false,
onConfirm,
confirmText = 'Okay, Got It',
cancelText = 'Cancel',
isLoading = false,
itemsList = [],
}: AlertModalProps) {
// Icon configurations for each variant - matching design from images
const iconConfig = {
success: (
<div className="relative flex items-center justify-center w-24 h-24 mx-auto mb-6">
{/* Light green flower-like outer shape with rounded petals */}
<div className="absolute inset-0 bg-success-100 rounded-full" style={{
clipPath: 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)',
width: '80px',
height: '80px'
}}></div>
{/* Dark green inner circle */}
<div className="relative bg-success-600 rounded-full w-16 h-16 flex items-center justify-center">
<svg
className="w-8 h-8 text-white"
fill="none"
stroke="currentColor"
strokeWidth={3}
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M5 13l4 4L19 7"
/>
</svg>
</div>
</div>
),
info: (
<div className="relative flex items-center justify-center w-24 h-24 mx-auto mb-6">
{/* Light blue cloud-like background */}
<div className="absolute inset-0 bg-blue-light-100 rounded-full blur-2xl opacity-50" style={{
width: '90px',
height: '90px',
transform: 'scale(1.1)'
}}></div>
{/* Blue circle with 'i' */}
<div className="relative bg-blue-light-500 rounded-full w-16 h-16 flex items-center justify-center shadow-lg">
<span className="text-white text-4xl font-bold leading-none">i</span>
</div>
</div>
),
warning: (
<div className="relative flex items-center justify-center w-24 h-24 mx-auto mb-6">
{/* Light orange cloud-like background */}
<div className="absolute inset-0 bg-warning-100 rounded-full blur-2xl opacity-50" style={{
width: '90px',
height: '90px',
transform: 'scale(1.1)'
}}></div>
{/* White circle */}
<div className="relative bg-white rounded-full w-14 h-14 flex items-center justify-center shadow-lg">
{/* Orange circle with exclamation */}
<div className="bg-warning-500 rounded-full w-16 h-16 flex items-center justify-center absolute -inset-1">
<span className="text-white text-4xl font-bold leading-none">!</span>
</div>
</div>
</div>
),
danger: (
<div className="relative flex items-center justify-center w-24 h-24 mx-auto mb-6">
{/* Light red cloud-like background */}
<div className="absolute inset-0 bg-error-100 rounded-full blur-2xl opacity-50" style={{
width: '90px',
height: '90px',
transform: 'scale(1.1)'
}}></div>
{/* Light red circle with red X */}
<div className="relative bg-error-100 rounded-full w-16 h-16 flex items-center justify-center">
<svg
className="w-10 h-10 text-error-500"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
fillRule="evenodd"
d="M18.364 5.636a1 1 0 010 1.414L13.414 12l4.95 4.95a1 1 0 11-1.414 1.414L12 13.414l-4.95 4.95a1 1 0 01-1.414-1.414L10.586 12 5.636 7.05a1 1 0 011.414-1.414L12 10.586l4.95-4.95a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
</div>
</div>
),
};
// Button color configurations
const buttonConfig = {
success: 'bg-success-500 hover:bg-success-600 text-white',
info: 'bg-blue-light-500 hover:bg-blue-light-600 text-white',
warning: 'bg-warning-500 hover:bg-warning-600 text-white',
danger: 'bg-error-500 hover:bg-error-600 text-white',
};
return (
<Modal
isOpen={isOpen}
onClose={onClose}
className="max-w-md"
>
<div className="px-8 py-10 text-center">
{/* Icon */}
{iconConfig[variant]}
{/* Title */}
<h2 className="text-2xl font-bold text-gray-800 dark:text-white mb-4">
{title}
</h2>
{/* Items List (for delete confirmations) */}
{itemsList.length > 0 && (
<div className="mb-6">
<ul className="text-left text-gray-700 dark:text-gray-300 text-sm space-y-1 max-w-md mx-auto">
{itemsList.slice(0, 5).map((item, index) => (
<li key={index} className="italic">
{item}
</li>
))}
{itemsList.length > 5 && (
<li className="text-gray-500 dark:text-gray-400 italic">
... and {itemsList.length - 5} more
</li>
)}
</ul>
</div>
)}
{/* Message */}
<p className="text-gray-600 dark:text-gray-400 mb-8 text-sm leading-relaxed">
{message}
</p>
{/* Buttons */}
{isConfirmation ? (
<div className="flex justify-center gap-3">
<button
onClick={onClose}
disabled={isLoading}
className="px-6 py-3 rounded-lg font-medium text-sm transition-colors shadow-sm bg-gray-200 hover:bg-gray-300 text-gray-700 dark:bg-gray-700 dark:hover:bg-gray-600 dark:text-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
{cancelText}
</button>
<button
onClick={onConfirm}
disabled={isLoading}
className={`px-6 py-3 rounded-lg font-medium text-sm transition-colors shadow-sm ${buttonConfig[variant]} disabled:opacity-50 disabled:cursor-not-allowed`}
>
{isLoading ? 'Processing...' : confirmText}
</button>
</div>
) : (
<div className="flex justify-center">
<button
onClick={onClose}
className={`px-6 py-3 rounded-lg font-medium text-sm transition-colors shadow-sm ${buttonConfig[variant]}`}
>
{buttonText}
</button>
</div>
)}
</div>
</Modal>
);
}