feat(owner-dash): Expand project command center with mock data, modals, and recharts fixes
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
const CreateItemModal = ({ isOpen, onClose, title, icon: Icon, iconColor = "text-[#00f0ff]", iconBg = "bg-blue-500/10", fields, onSubmit, submitLabel = "Submit", submitColor = "bg-blue-500/20 text-[#00f0ff] border-blue-500/30" }) => {
|
||||
const [formData, setFormData] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const handleEsc = (e) => { if (e.key === 'Escape') onClose(); };
|
||||
if (isOpen) window.addEventListener('keydown', handleEsc);
|
||||
return () => window.removeEventListener('keydown', handleEsc);
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onSubmit(formData);
|
||||
setFormData({});
|
||||
onClose();
|
||||
};
|
||||
|
||||
return createPortal(
|
||||
<div className="fixed inset-0 z-[9999] flex items-center justify-center p-4" role="dialog" aria-modal="true">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onClose} />
|
||||
<div className="relative w-full max-w-md bg-[#121214] rounded-2xl shadow-[0_0_40px_rgba(0,0,0,0.8)] border border-white/10 overflow-hidden animate-in zoom-in-95 duration-200">
|
||||
{/* Header */}
|
||||
<div className="px-6 py-5 border-b border-white/10 flex justify-between items-center bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
{Icon && (
|
||||
<div className={`p-2 rounded-xl ${iconBg} ${iconColor}`}>
|
||||
<Icon size={20} />
|
||||
</div>
|
||||
)}
|
||||
<h2 className="text-lg font-bold text-white tracking-widest uppercase">{title}</h2>
|
||||
</div>
|
||||
<button onClick={onClose} className="p-2 rounded-lg hover:bg-white/10 text-zinc-500 hover:text-white transition-colors">
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<form onSubmit={handleSubmit} className="p-6 space-y-5">
|
||||
{fields.map((field, i) => (
|
||||
<div key={i} className="space-y-1.5">
|
||||
<label className="text-xs font-bold uppercase tracking-wider text-zinc-400">{field.label}</label>
|
||||
{field.type === 'textarea' ? (
|
||||
<textarea
|
||||
className="w-full bg-[#18181b] border border-white/10 rounded-xl px-4 py-3 text-sm text-white placeholder-zinc-600 focus:outline-none focus:border-[#00f0ff] focus:ring-1 focus:ring-[#00f0ff] transition-all"
|
||||
rows={4}
|
||||
placeholder={field.placeholder || ''}
|
||||
required={field.required}
|
||||
value={formData[field.name] || ''}
|
||||
onChange={e => setFormData({ ...formData, [field.name]: e.target.value })}
|
||||
/>
|
||||
) : field.type === 'select' ? (
|
||||
<select
|
||||
className="w-full bg-[#18181b] border border-white/10 rounded-xl px-4 py-3 text-sm text-white focus:outline-none focus:border-[#00f0ff] focus:ring-1 focus:ring-[#00f0ff] transition-all"
|
||||
required={field.required}
|
||||
value={formData[field.name] || ''}
|
||||
onChange={e => setFormData({ ...formData, [field.name]: e.target.value })}
|
||||
>
|
||||
<option value="" disabled>Select {field.label}</option>
|
||||
{field.options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
|
||||
</select>
|
||||
) : field.type === 'file' ? (
|
||||
<input
|
||||
type="file"
|
||||
accept={field.accept}
|
||||
className="w-full text-sm text-zinc-400 file:mr-4 file:py-2.5 file:px-4 file:rounded-xl file:border file:border-white/10 file:text-sm file:font-bold file:bg-[#18181b] file:text-white hover:file:bg-white/5 transition-all"
|
||||
required={field.required}
|
||||
onChange={e => setFormData({ ...formData, [field.name]: e.target.files[0] })}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
type={field.type || 'text'}
|
||||
className="w-full bg-[#18181b] border border-white/10 rounded-xl px-4 py-3 text-sm text-white placeholder-zinc-600 focus:outline-none focus:border-[#00f0ff] focus:ring-1 focus:ring-[#00f0ff] transition-all"
|
||||
placeholder={field.placeholder || ''}
|
||||
required={field.required}
|
||||
value={formData[field.name] || ''}
|
||||
onChange={e => setFormData({ ...formData, [field.name]: e.target.value })}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Footer */}
|
||||
<div className="pt-4 flex justify-end gap-3">
|
||||
<button type="button" onClick={onClose} className="px-5 py-2.5 text-sm font-bold text-zinc-400 hover:text-white transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" className={`px-6 py-2.5 text-sm font-bold rounded-xl border ${submitColor} hover:brightness-110 transition-all shadow-[0_0_15px_rgba(0,0,0,0.5)]`}>
|
||||
{submitLabel}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateItemModal;
|
||||
Reference in New Issue
Block a user