feat: apply theme colors to all pages and modals - implement light/dark theme CSS variables across UI components

This commit is contained in:
2026-06-23 18:28:05 +07:00
parent 7182391241
commit a64af5f9cf
18 changed files with 458 additions and 311 deletions
+42 -42
View File
@@ -45,13 +45,13 @@ const MapPicker = ({ onPick, center }: { onPick: (latlng: L.LatLng) => void, cen
{menuPos && (
<div
ref={menuRef}
className="absolute z-[3000] bg-white rounded-xl shadow-xl border border-gray-100 py-1 w-44 animate-in zoom-in-95 duration-200"
className="absolute z-[3000] bg-[var(--surface)] rounded-xl shadow-xl border border-[var(--border)] py-1 w-44 animate-in zoom-in-95 duration-200"
style={{ top: menuPos.y, left: menuPos.x }}
>
<button
type="button"
onClick={() => { onPick(menuPos.latlng); setMenuPos(null); }}
className="w-full text-left px-4 py-2 hover:bg-blue-50 text-xs font-bold text-blue-600 flex items-center gap-2"
className="w-full text-left px-4 py-2 hover:bg-[var(--background)] text-xs font-bold text-blue-600 flex items-center gap-2"
>
<MapPin className="w-3 h-3" /> Thêm vào chặng hiện tại
</button>
@@ -387,18 +387,18 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
return (
<div className="fixed inset-0 z-[2000] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm" onClick={onClose} />
<div className="relative w-full max-w-lg bg-white rounded-3xl shadow-2xl overflow-hidden p-8 max-h-[90vh] overflow-y-auto">
<div className="relative w-full max-w-lg bg-[var(--surface)] rounded-3xl shadow-2xl overflow-hidden p-8 max-h-[90vh] overflow-y-auto">
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
<h2 className="text-2xl font-bold text-[var(--text-primary)] flex items-center gap-2">
<MapIcon className="w-6 h-6 text-blue-600" /> {titleText}
</h2>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-full transition-colors">
<X className="w-6 h-6 text-gray-400" />
<button onClick={onClose} className="p-2 hover:bg-[var(--background)] rounded-full transition-colors">
<X className="w-6 h-6 text-[var(--text-muted)]" />
</button>
</div>
{/* Mini Map Picker */}
<div className="h-64 w-full rounded-3xl overflow-hidden mb-6 border border-gray-100 relative shadow-xl group">
<div className="h-64 w-full rounded-3xl overflow-hidden mb-6 border border-[var(--border)] relative shadow-xl group">
{/* Map Search Bar Overlay - Tích hợp tìm kiếm trực tiếp trên bản đồ */}
<div className="absolute top-3 left-3 right-3 z-[1001] pointer-events-none">
<div className="relative max-w-sm pointer-events-auto">
@@ -406,7 +406,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
<input
type="text"
placeholder="Tìm địa điểm trên bản đồ..."
className="w-full pl-11 pr-10 py-3 bg-white/95 backdrop-blur-md border border-white/20 rounded-2xl shadow-lg outline-none focus:ring-2 focus:ring-blue-500 transition-all text-sm font-bold text-gray-800"
className="w-full pl-11 pr-10 py-3 bg-white/95 backdrop-blur-md border border-white/20 rounded-2xl shadow-lg outline-none focus:ring-2 focus:ring-blue-500 transition-all text-sm font-bold text-[var(--text-primary)]"
value={formData.name}
onChange={e => handleSearchLocation(e.target.value)}
/>
@@ -417,7 +417,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
<button
type="button"
onClick={() => { setFormData({...formData, name: ''}); setSearchResults([]); setHasNoResults(false); }}
className="absolute right-3 top-1/2 -translate-y-1/2 p-1.5 hover:bg-gray-100 rounded-full text-gray-400 transition-colors"
className="absolute right-3 top-1/2 -translate-y-1/2 p-1.5 hover:bg-[var(--background)] rounded-full text-[var(--text-muted)] transition-colors"
>
<X className="w-4 h-4" />
</button>
@@ -426,24 +426,24 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
{/* Dropdown kết quả tìm kiếm ngay trong khung bản đồ */}
{(searchResults.length > 0 || hasNoResults) && (
<div className="absolute left-0 right-0 mt-2 bg-white/95 backdrop-blur-md border border-gray-100 rounded-2xl shadow-2xl overflow-hidden max-h-40 overflow-y-auto animate-in slide-in-from-top-2 duration-200">
<div className="absolute left-0 right-0 mt-2 bg-white/95 backdrop-blur-md border border-[var(--border)] rounded-2xl shadow-2xl overflow-hidden max-h-40 overflow-y-auto animate-in slide-in-from-top-2 duration-200">
{hasNoResults ? (
<div className="px-4 py-4 text-center text-gray-400 text-xs italic">Không tìm thấy đa điểm phù hợp...</div>
<div className="px-4 py-4 text-center text-[var(--text-muted)] text-xs italic">Không tìm thấy đa điểm phù hợp...</div>
) : (
searchResults.map((result, idx) => (
<button
key={idx}
type="button"
onClick={() => selectSearchResult(result)}
className="w-full text-left px-4 py-3 hover:bg-blue-50 border-b border-gray-50 last:border-0 transition-colors flex flex-col gap-0.5"
className="w-full text-left px-4 py-3 hover:bg-[var(--background)] border-b border-[var(--border)] last:border-0 transition-colors flex flex-col gap-0.5"
>
<div className="flex items-center justify-between gap-2">
<div className="font-bold text-xs text-gray-900 truncate">{result.namedetails?.name || result.display_name.split(',')[0]}</div>
<div className="font-bold text-xs text-[var(--text-primary)] truncate">{result.namedetails?.name || result.display_name.split(',')[0]}</div>
{result.type && (
<span className="text-[8px] font-black uppercase text-blue-400 bg-blue-50 px-1.5 py-0.5 rounded border border-blue-100 shrink-0">{result.type}</span>
)}
</div>
<div className="text-[10px] text-gray-500 truncate leading-tight">{result.display_name}</div>
<div className="text-[10px] text-[var(--text-muted)] truncate leading-tight">{result.display_name}</div>
</button>
))
)}
@@ -457,7 +457,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
<Marker position={currentCoords} />
<MapPicker center={currentCoords} onPick={handlePickLocation} />
</MapContainer>
<div className="absolute bottom-2 left-2 z-[1000] bg-white/90 backdrop-blur-sm px-2 py-1 rounded-lg text-[10px] font-black text-gray-500 shadow-sm border border-gray-100">
<div className="absolute bottom-2 left-2 z-[1000] bg-white/90 backdrop-blur-sm px-2 py-1 rounded-lg text-[10px] font-black text-[var(--text-muted)] shadow-sm border border-[var(--border)]">
CHUỘT PHẢI Đ CHỌN VỊ TRÍ
</div>
</div>
@@ -475,23 +475,23 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Tên đa điểm</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Tên đa điểm</label>
<input
required
placeholder="Tên địa điểm..."
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 transition-all font-bold"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none focus:ring-2 focus:ring-blue-500 transition-all font-bold"
value={formData.name}
onChange={e => setFormData({...formData, name: e.target.value})}
/>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Đa chỉ</label>
<input className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Address</label>
<input className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none"
value={formData.address} onChange={e => setFormData({...formData, address: e.target.value})} />
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Ghi chú đa điểm / Dịch vụ sử dụng</label>
<textarea className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none resize-none" rows={2}
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Ghi chú đa điểm / Dịch vụ sử dụng</label>
<textarea className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none resize-none" rows={2}
placeholder="Ví dụ: Ăn trưa tại quán X, thuê hướng dẫn viên..."
value={formData.note} onChange={e => setFormData({...formData, note: e.target.value})} />
</div>
@@ -499,8 +499,8 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
<p className="text-xs font-black text-blue-500 uppercase tracking-widest">Chi phí nhanh tại điểm này</p>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Số tiền (VNĐ)</label>
<input type="text" className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-xs font-bold text-[var(--text-secondary)] mb-1">Số tiền (VNĐ)</label>
<input type="text" className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
placeholder="0"
value={formData.expenseAmount} onChange={e => {
const rawValue = e.target.value.replace(/\D/g, ""); // Chỉ lấy số
@@ -509,8 +509,8 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
}} />
</div>
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Loại dịch vụ</label>
<select className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-xs font-bold text-[var(--text-secondary)] mb-1">Loại dịch vụ</label>
<select className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
value={formData.expenseCategory} onChange={e => setFormData({...formData, expenseCategory: e.target.value})}>
<option value="FOOD">Ăn uống</option>
<option value="TRANSPORT">Di chuyển</option>
@@ -521,20 +521,20 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
</div>
</div>
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Dịch vụ / tả</label>
<input className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-xs font-bold text-[var(--text-secondary)] mb-1">Dịch vụ / tả</label>
<input className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
placeholder="Ví dụ: Ăn trưa, taxi, vé..."
value={formData.expenseDescription} onChange={e => setFormData({...formData, expenseDescription: e.target.value})} />
</div>
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Ghi chú chi phí</label>
<textarea className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none resize-none text-sm" rows={2}
<label className="block text-xs font-bold text-[var(--text-secondary)] mb-1">Ghi chú chi phí</label>
<textarea className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none resize-none text-sm" rows={2}
placeholder="Ghi chú thêm..."
value={formData.expenseNote} onChange={e => setFormData({...formData, expenseNote: e.target.value})} />
</div>
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Thành viên đã thanh toán</label>
<select className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-xs font-bold text-[var(--text-secondary)] mb-1">Thành viên đã thanh toán</label>
<select className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
value={formData.paidById} onChange={e => setFormData({...formData, paidById: e.target.value})}>
<option value="">-- Chọn người thanh toán --</option>
{currentTour?.participants?.filter((p: any) => p.user || p.displayName)?.map((p: any) => {
@@ -549,8 +549,8 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
</div>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Gán vào chặng</label>
<select required className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Gán vào chặng</label>
<select required className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none"
value={currentLegId} onChange={e => setFormData({...formData, legId: e.target.value})}>
{legs.map(leg => (
<option key={leg.id} value={leg.id}>
@@ -562,26 +562,26 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-bold text-gray-700 mb-1"> đ</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1"> đ</label>
<input
type="number"
step="any"
required
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none"
value={formData.latitude}
onChange={e => setFormData({...formData, latitude: e.target.value as any})}
onPaste={(e) => handleCoordinatePaste(e, 'latitude')}
placeholder="13.50731401913824"
/>
<p className="text-xs text-gray-400 mt-1">💡 Dán "lat, lng" đ tự đng điền cả đ kinh đ</p>
<p className="text-xs text-[var(--text-muted)] mt-1">💡 Dán "lat, lng" đ tự Đng điền cả đ kinh đ</p>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Kinh đ</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Kinh đ</label>
<input
type="number"
step="any"
required
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none"
value={formData.longitude}
onChange={e => setFormData({...formData, longitude: e.target.value as any})}
onPaste={(e) => handleCoordinatePaste(e, 'longitude')}
@@ -590,8 +590,8 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
</div>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Loại</label>
<select className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Loại</label>
<select className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none"
value={formData.type} onChange={e => setFormData({...formData, type: e.target.value as any})}>
<option value="VISIT">Tham quan</option>
<option value="EAT">Ăn uống</option>
@@ -601,8 +601,8 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Bắt đu</label>
<input type="datetime-local" className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Bắt đu</label>
<input type="datetime-local" className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none"
value={formData.plannedStart} onChange={e => setFormData({...formData, plannedStart: e.target.value})} />
</div>
</div>
+8 -8
View File
@@ -174,12 +174,12 @@ export const AddPhotoModal: React.FC<AddPhotoModalProps> = ({ isOpen, onClose, t
return (
<div className="fixed inset-0 z-[2500] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm" onClick={onClose} />
<div className="relative w-full max-w-lg bg-white rounded-3xl shadow-2xl overflow-hidden p-8 max-h-[90vh] flex flex-col animate-in zoom-in-95 duration-200">
<div className="relative w-full max-w-lg bg-[var(--surface)] rounded-3xl shadow-2xl overflow-hidden p-8 max-h-[90vh] flex flex-col animate-in zoom-in-95 duration-200">
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
<h2 className="text-2xl font-bold text-[var(--text-primary)] flex items-center gap-2">
<ImageIcon className="w-6 h-6 text-blue-600" /> Tải nh lên
</h2>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-full transition-colors text-gray-400">
<button onClick={onClose} className="p-2 hover:bg-[var(--background)] rounded-full transition-colors text-[var(--text-muted)]">
<X className="w-6 h-6" />
</button>
</div>
@@ -187,22 +187,22 @@ export const AddPhotoModal: React.FC<AddPhotoModalProps> = ({ isOpen, onClose, t
<form onSubmit={handleSubmit} className="flex-1 flex flex-col min-h-0">
<div
onClick={() => fileInputRef.current?.click()}
className="border-2 border-dashed border-gray-200 rounded-[32px] p-10 flex flex-col items-center justify-center cursor-pointer hover:bg-blue-50/50 hover:border-blue-200 transition-all mb-6 group"
className="border-2 border-dashed border-[var(--border)] rounded-[32px] p-10 flex flex-col items-center justify-center cursor-pointer hover:bg-[var(--background)]/50 hover:border-blue-200 transition-all mb-6 group"
>
<input type="file" ref={fileInputRef} className="hidden" multiple accept="image/*" onChange={handleFileChange} />
<div className="w-16 h-16 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-600 mb-4 group-hover:scale-110 transition-transform shadow-inner">
<Upload className="w-8 h-8" />
</div>
<p className="text-sm font-black text-gray-700">Nhấn đ chọn nh</p>
<p className="text-xs text-gray-400 mt-1 font-medium">Hỗ trợ JPG, PNG, WEBP</p>
<p className="text-sm font-black text-[var(--text-secondary)]">Nhấn đ chọn nh</p>
<p className="text-xs text-[var(--text-muted)] mt-1 font-medium">Hỗ trợ JPG, PNG, WEBP</p>
</div>
{previews.length > 0 && (
<div className="flex-1 overflow-y-auto mb-6 pr-2">
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest mb-3">Đã chọn {previews.length} tệp</p>
<p className="text-[10px] font-black text-[var(--text-muted)] uppercase tracking-widest mb-3">Đã chọn {previews.length} tệp</p>
<div className="grid grid-cols-3 gap-3">
{previews.map((src, idx) => (
<div key={idx} className="relative aspect-square rounded-2xl overflow-hidden border border-gray-100 shadow-sm group">
<div key={idx} className="relative aspect-square rounded-2xl overflow-hidden border border-[var(--border)] shadow-sm group">
<img src={src} className="w-full h-full object-cover" alt="preview" />
<button
type="button"
+15 -15
View File
@@ -134,27 +134,27 @@ export const CommentModal: React.FC<CommentModalProps> = ({ isOpen, onClose, loc
return (
<div className="fixed inset-0 z-[5000] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm animate-in fade-in duration-200" onClick={onClose} />
<div className="relative w-full max-w-md bg-white rounded-[32px] shadow-2xl overflow-hidden flex flex-col max-h-[80vh] animate-in zoom-in-95 duration-200">
<div className="relative w-full max-w-md bg-[var(--surface)] rounded-[32px] shadow-2xl overflow-hidden flex flex-col max-h-[80vh] animate-in zoom-in-95 duration-200">
{/* Header */}
<div className="p-6 border-b border-gray-100 flex justify-between items-center bg-white sticky top-0 z-10">
<div className="p-6 border-b border-[var(--border)] flex justify-between items-center bg-[var(--surface)] sticky top-0 z-10">
<div>
<h3 className="text-xl font-black text-gray-900 flex items-center gap-2">
<h3 className="text-xl font-black text-[var(--text-primary)] flex items-center gap-2">
<MessageSquare className="w-5 h-5 text-blue-600" />
Bình luận
</h3>
<p className="text-xs text-gray-500 font-bold uppercase tracking-widest mt-1 truncate max-w-[250px]">{locationName}</p>
<p className="text-xs text-[var(--text-muted)] font-bold uppercase tracking-widest mt-1 truncate max-w-[250px]">{locationName}</p>
</div>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-full transition-colors">
<X className="w-5 h-5 text-gray-400" />
<button onClick={onClose} className="p-2 hover:bg-[var(--background)] rounded-full transition-colors">
<X className="w-5 h-5 text-[var(--text-muted)]" />
</button>
</div>
{/* Comment List */}
<div className="flex-1 overflow-y-auto p-6 space-y-4 bg-gray-50/50">
<div className="flex-1 overflow-y-auto p-6 space-y-4 bg-[var(--background)]/50">
{isLoading ? (
<div className="flex justify-center py-10"><Loader2 className="w-6 h-6 animate-spin text-blue-600" /></div>
) : comments.length === 0 ? (
<div className="text-center py-10 text-gray-400 italic text-sm">Chưa bình luận nào.</div>
<div className="text-center py-10 text-[var(--text-muted)] italic text-sm">Chưa bình luận nào.</div>
) : (
comments.map((c) => (
<div key={c.id} className="flex gap-3 animate-in slide-in-from-left-2 duration-300">
@@ -162,21 +162,21 @@ export const CommentModal: React.FC<CommentModalProps> = ({ isOpen, onClose, loc
<User className="w-4 h-4 text-blue-600" />
</div>
<div className="flex-1">
<div className="bg-white p-3 rounded-2xl rounded-tl-none border border-gray-100 shadow-sm">
<div className="bg-[var(--surface)] p-3 rounded-2xl rounded-tl-none border border-[var(--border)] shadow-sm">
<div className="flex justify-between items-start mb-1">
<p className="text-xs font-black text-gray-900">{c.userName}</p>
<p className="text-xs font-black text-[var(--text-primary)]">{c.userName}</p>
{(userRole === 'OWNER' || userRole === 'MANAGER' || c.userId === currentUserId) && (
<button
onClick={() => setConfirmState({ open: true, commentId: c.id })}
className="text-gray-400 hover:text-red-500 transition-colors"
className="text-[var(--text-muted)] hover:text-red-500 transition-colors"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
)}
</div>
<p className="text-sm text-gray-600 leading-relaxed">{c.content}</p>
<p className="text-sm text-[var(--text-secondary)] leading-relaxed">{c.content}</p>
</div>
<p className="text-[10px] text-gray-400 mt-1 ml-1 font-medium">
<p className="text-[10px] text-[var(--text-muted)] mt-1 ml-1 font-medium">
{new Date(c.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
</div>
@@ -186,7 +186,7 @@ export const CommentModal: React.FC<CommentModalProps> = ({ isOpen, onClose, loc
</div>
{/* Input Area */}
<div className="p-4 bg-white border-t border-gray-100">
<div className="p-4 bg-[var(--surface)] border-t border-[var(--border)]">
<div className="relative flex items-center gap-2">
<input
type="text"
@@ -194,7 +194,7 @@ export const CommentModal: React.FC<CommentModalProps> = ({ isOpen, onClose, loc
onChange={(e) => setNewComment(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
placeholder={isPublicView ? 'Đăng nhập để bình luận...' : 'Viết bình luận...'}
className="flex-1 bg-gray-50 border border-gray-200 rounded-2xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
className="flex-1 bg-[var(--background)] border border-[var(--border)] rounded-2xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-[var(--surface)] transition-all"
/>
<button onClick={handleSend} disabled={!newComment.trim() || isPublicView} className="p-3 bg-blue-600 text-white rounded-2xl hover:bg-blue-700 disabled:opacity-50 disabled:bg-gray-300 transition-all active:scale-95 shadow-lg shadow-blue-100">
<Send className="w-4 h-4" />
+6 -6
View File
@@ -18,25 +18,25 @@ export const ConfirmModal: React.FC<ConfirmModalProps> = ({ isOpen, title, messa
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm animate-in fade-in duration-200" onClick={onCancel} />
{/* Modal Content */}
<div className="relative w-full max-w-sm bg-white rounded-[32px] shadow-2xl p-8 animate-in zoom-in-95 duration-200">
<div className="relative w-full max-w-sm bg-[var(--surface)] rounded-[32px] shadow-2xl p-8 animate-in zoom-in-95 duration-200">
<div className="flex justify-between items-center mb-4">
<div className="w-12 h-12 rounded-2xl bg-red-50 flex items-center justify-center text-red-500 shadow-inner">
<AlertTriangle className="w-6 h-6" />
</div>
<button onClick={onCancel} className="p-2 hover:bg-gray-100 rounded-full transition-colors">
<X className="w-5 h-5 text-gray-400" />
<button onClick={onCancel} className="p-2 hover:bg-[var(--background)] rounded-full transition-colors">
<X className="w-5 h-5 text-[var(--text-muted)]" />
</button>
</div>
<h3 className="text-xl font-black text-gray-900 mb-2">{title || 'Xác nhận'}</h3>
<p className="text-sm text-gray-500 mb-8 leading-relaxed">
<h3 className="text-xl font-black text-[var(--text-primary)] mb-2">{title || 'Xác nhận'}</h3>
<p className="text-sm text-[var(--text-muted)] mb-8 leading-relaxed">
{message || 'Bạn có chắc chắn muốn thực hiện hành động này không?'}
</p>
<div className="grid grid-cols-2 gap-3">
<button
onClick={onCancel}
className="py-4 bg-gray-100 hover:bg-gray-200 text-gray-600 font-bold rounded-2xl transition-all active:scale-95"
className="py-4 bg-[var(--background)] hover:bg-[var(--background)] text-[var(--text-secondary)] font-bold rounded-2xl transition-all active:scale-95"
>
Hủy bỏ
</button>
+31 -31
View File
@@ -103,18 +103,18 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
return (
<div className="fixed inset-0 z-[2000] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm" onClick={onClose} />
<div className="relative w-full max-w-md bg-white rounded-3xl shadow-2xl overflow-hidden flex flex-col max-h-[90vh]">
<div className="p-5 border-b border-gray-100 flex justify-between items-center bg-gray-50/50">
<h2 className="text-xl font-bold text-gray-900">Tạo Tour mới</h2>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-full transition-colors"></button>
<div className="relative w-full max-w-md bg-[var(--surface)] rounded-3xl shadow-2xl overflow-hidden flex flex-col max-h-[90vh]">
<div className="p-5 border-b border-[var(--border)] flex justify-between items-center bg-[var(--background)]/50">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Tạo Tour mới</h2>
<button onClick={onClose} className="p-2 hover:bg-[var(--background)] rounded-full transition-colors"></button>
</div>
<form onSubmit={handleSubmit} className="p-5 space-y-4 overflow-y-auto flex-1">
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Tên Tour</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Tên Tour</label>
<input
required
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="VD: Khám phá Đà Lạt"
@@ -122,7 +122,7 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-2 flex items-center gap-2">
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-2 flex items-center gap-2">
<TagIcon className="w-4 h-4" /> Phân loại Tour
</label>
<div className="flex flex-wrap gap-2">
@@ -134,7 +134,7 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
className={`px-3 py-1.5 rounded-full text-xs font-bold transition-all border ${
selectedTags.includes(tag)
? 'bg-blue-600 text-white border-blue-600 shadow-md shadow-blue-100'
: 'bg-white text-gray-500 border-gray-200 hover:border-blue-300'
: 'bg-[var(--surface)] text-[var(--text-secondary)] border-[var(--border)] hover:border-blue-300'
}`}
>
{tag}
@@ -148,7 +148,7 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
onChange={(e) => setCustomTag(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), addCustomTag())}
placeholder="Thêm nhãn tùy chỉnh..."
className="flex-1 px-3 py-2 bg-gray-50 border border-gray-100 rounded-xl text-xs outline-none focus:ring-2 focus:ring-blue-500"
className="flex-1 px-3 py-2 bg-[var(--background)] border border-[var(--border)] rounded-xl text-xs outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="button"
@@ -161,9 +161,9 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1"> tả chuyến đi</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1"> tả chuyến đi</label>
<textarea
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 resize-none text-sm"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none focus:ring-2 focus:ring-blue-500 resize-none text-sm"
rows={3}
value={description}
onChange={(e) => setDescription(e.target.value)}
@@ -173,19 +173,19 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Bắt đu</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Bắt đu</label>
<input
type="date"
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">Kết thúc</label>
<label className="block text-sm font-bold text-[var(--text-secondary)] mb-1">Kết thúc</label>
<input
type="date"
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
/>
@@ -199,18 +199,18 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
</div>
<div className="grid grid-cols-3 gap-3">
<div>
<label className="block text-[10px] font-bold text-gray-500 uppercase mb-1">Người lớn</label>
<input type="number" className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-[10px] font-bold text-[var(--text-muted)] uppercase mb-1">Người lớn</label>
<input type="number" className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
value={adultCount} onChange={e => setAdultCount(Number(e.target.value))} />
</div>
<div>
<label className="block text-[10px] font-bold text-gray-500 uppercase mb-1">Trẻ em</label>
<input type="number" className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-[10px] font-bold text-[var(--text-muted)] uppercase mb-1">Trẻ em</label>
<input type="number" className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
value={childCount} onChange={e => setChildCount(Number(e.target.value))} />
</div>
<div>
<label className="block text-[10px] font-bold text-gray-500 uppercase mb-1">Giảm trẻ em %</label>
<input type="number" className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<label className="block text-[10px] font-bold text-[var(--text-muted)] uppercase mb-1">Giảm trẻ em %</label>
<input type="number" className="w-full px-3 py-2 bg-[var(--surface)] border border-[var(--border)] rounded-xl outline-none text-sm"
value={childDiscount} onChange={e => setChildDiscount(Number(e.target.value))} />
</div>
</div>
@@ -218,9 +218,9 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
</div>
<div className="space-y-2">
<label className="block text-sm font-bold text-gray-700">Thành viên tham gia ({members.length})</label>
<label className="block text-sm font-bold text-[var(--text-secondary)]">Thành viên tham gia ({members.length})</label>
{members.length > 0 && (
<div className="flex flex-wrap gap-3 mb-3 p-3 bg-gray-50 rounded-2xl border border-gray-100">
<div className="flex flex-wrap gap-3 mb-3 p-3 bg-[var(--background)] rounded-2xl border border-[var(--border)]">
{members.map((m) => {
const initial = m.name?.charAt(0) || '?';
return (
@@ -232,13 +232,13 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
<button
type="button"
onClick={() => removeMember(m.id)}
className="absolute -top-1 -right-1 w-4 h-4 bg-gray-500 hover:bg-red-600 rounded-full flex items-center justify-center text-white"
className="absolute -top-1 -right-1 w-4 h-4 bg-gray-400 hover:bg-red-600 rounded-full flex items-center justify-center text-white"
aria-label="Remove item"
>
<Trash2 size={10} />
</button>
</div>
<span className="text-[10px] font-semibold text-gray-700 max-w-[72px] truncate">{m.name}</span>
<span className="text-[10px] font-semibold text-[var(--text-secondary)] max-w-[72px] truncate">{m.name}</span>
</div>
);
})}
@@ -247,13 +247,13 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
<div className="relative">
<input
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm font-bold text-gray-800"
className="w-full px-4 py-3 bg-[var(--background)] border border-[var(--border)] rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-sm font-bold text-[var(--text-primary)]"
placeholder="Tìm email hoặc nhập tên thành viên ngoài hệ thống..."
value={query}
onChange={(e) => searchUsers(e.target.value)}
/>
{(results.length > 0 || query.trim()) && (
<div className="absolute bottom-full mb-2 left-0 right-0 bg-white border border-gray-100 rounded-2xl shadow-xl z-20 max-h-48 overflow-y-auto p-2 space-y-1">
<div className="absolute bottom-full mb-2 left-0 right-0 bg-[var(--surface)] border border-[var(--border)] rounded-2xl shadow-xl z-20 max-h-48 overflow-y-auto p-2 space-y-1">
{query.trim() && (
<button
type="button"
@@ -263,7 +263,7 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
setQuery('');
setResults([]);
}}
className="w-full text-left px-3 py-2 text-sm hover:bg-blue-50 rounded-xl text-blue-600 font-bold flex items-center gap-2"
className="w-full text-left px-3 py-2 text-sm hover:bg-[var(--background)] rounded-xl text-blue-600 font-bold flex items-center gap-2"
>
<span className="flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-600 text-sm font-black">+</span>
<span>Thêm thành viên ngoài hệ thống: "{query.trim()}"</span>
@@ -274,10 +274,10 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
key={u.id}
type="button"
onClick={() => confirmAddMember(u)}
className="w-full text-left px-3 py-2 text-sm hover:bg-blue-50 rounded-xl flex flex-col"
className="w-full text-left px-3 py-2 text-sm hover:bg-[var(--background)] rounded-xl flex flex-col"
>
<span className="font-bold text-gray-900">{u.name || 'Chưa đặt tên'}</span>
<span className="text-xs text-gray-500">{u.email}</span>
<span className="font-bold text-[var(--text-primary)]">{u.name || 'Chưa đặt tên'}</span>
<span className="text-xs text-[var(--text-muted)]">{u.email}</span>
</button>
))}
</div>
+12 -12
View File
@@ -209,7 +209,7 @@ export const JoinTourLoginModal: React.FC<JoinTourLoginModalProps> = ({
return (
<div className="fixed inset-0 z-[3000] flex items-center justify-center p-4 bg-black/50">
<div className="w-full max-w-md bg-white rounded-[32px] shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200">
<div className="w-full max-w-md bg-[var(--surface)] rounded-[32px] shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200">
{/* Header */}
<div className="relative h-32 bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 overflow-hidden">
<div className="absolute inset-0 opacity-10">
@@ -228,10 +228,10 @@ export const JoinTourLoginModal: React.FC<JoinTourLoginModalProps> = ({
{/* Content */}
<div className="p-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2 text-center">
<h2 className="text-3xl font-bold text-[var(--text-primary)] mb-2 text-center">
Gia nhập tour
</h2>
<p className="text-center text-gray-600 mb-6">
<p className="text-center text-[var(--text-secondary)] mb-6">
Đăng nhập đ tham gia chuyến du lịch này
</p>
@@ -248,45 +248,45 @@ export const JoinTourLoginModal: React.FC<JoinTourLoginModalProps> = ({
<div className="relative mb-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-200" />
<div className="w-full border-t border-[var(--border)]" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white text-gray-500">Hoặc</span>
<span className="px-2 bg-[var(--surface)] text-[var(--text-muted)]">Hoặc</span>
</div>
</div>
{/* Email/Password Form */}
<form onSubmit={handleEmailPasswordJoin} className="space-y-4">
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
<label className="block text-sm font-semibold text-[var(--text-secondary)] mb-2">
Email
</label>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5 text-gray-400" />
<Mail className="absolute left-3 top-3.5 w-5 h-5 text-[var(--text-muted)]" />
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="your@email.com"
required
className="w-full pl-10 pr-4 py-2.5 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition"
className="w-full pl-10 pr-4 py-2.5 border border-[var(--border)] rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition bg-[var(--background)]"
/>
</div>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
<label className="block text-sm font-semibold text-[var(--text-secondary)] mb-2">
Mật khẩu
</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5 text-gray-400" />
<Lock className="absolute left-3 top-3.5 w-5 h-5 text-[var(--text-muted)]" />
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Nhập mật khẩu"
required
className="w-full pl-10 pr-4 py-2.5 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition"
className="w-full pl-10 pr-4 py-2.5 border border-[var(--border)] rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition bg-[var(--background)]"
/>
</div>
</div>
@@ -311,7 +311,7 @@ export const JoinTourLoginModal: React.FC<JoinTourLoginModalProps> = ({
</form>
{/* Signup Link */}
<div className="mt-6 text-center text-sm text-gray-600">
<div className="mt-6 text-center text-sm text-[var(--text-secondary)]">
Chưa tài khoản?{' '}
<button
onClick={() => {
+14 -14
View File
@@ -172,16 +172,16 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose, onSwitc
/>
{/* Modal Content */}
<div className="relative w-full max-w-md bg-white rounded-3xl shadow-2xl overflow-hidden max-h-[90vh] overflow-y-auto animate-in zoom-in-95 duration-300">
<div className="relative w-full max-w-md bg-[var(--surface)] rounded-3xl shadow-2xl overflow-hidden max-h-[90vh] overflow-y-auto animate-in zoom-in-95 duration-300">
<div className="p-8 sm:p-10">
<div className="flex justify-between items-start mb-8">
<div>
<h2 className="text-3xl font-bold text-gray-900">Đăng nhập</h2>
<p className="text-gray-500 mt-2">Chào mừng bạn quay trở lại!</p>
<h2 className="text-3xl font-bold text-[var(--text-primary)]">Đăng nhập</h2>
<p className="text-[var(--text-secondary)] mt-2">Chào mừng bạn quay trở lại!</p>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-gray-100 rounded-full transition-colors text-gray-400 hover:text-gray-600"
className="p-2 hover:bg-[var(--background)] rounded-full transition-colors text-[var(--text-muted)] hover:text-[var(--text-secondary)]"
>
<X className="w-6 h-6" />
</button>
@@ -195,34 +195,34 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose, onSwitc
<form className="space-y-6" onSubmit={handleSubmit}>
<div className="space-y-2">
<label className="text-sm font-semibold text-gray-700 ml-1">Tài khoản hoặc Email</label>
<label className="text-sm font-semibold text-[var(--text-secondary)] ml-1">Tài khoản hoặc Email</label>
<div className="relative group">
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" />
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-[var(--text-muted)] group-focus-within:text-blue-500 transition-colors" />
<input
type="text"
placeholder="admin hoặc email..."
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all"
className="w-full pl-12 pr-4 py-4 bg-[var(--background)] border border-[var(--border)] rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-[var(--surface)] outline-none transition-all"
/>
</div>
</div>
<div className="space-y-2">
<div className="flex justify-between items-center px-1">
<label className="text-sm font-semibold text-gray-700">Mật khẩu</label>
<label className="text-sm font-semibold text-[var(--text-secondary)]">Mật khẩu</label>
<button className="text-xs font-bold text-blue-600 hover:text-blue-700">Quên mật khẩu?</button>
</div>
<div className="relative group">
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" />
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-[var(--text-muted)] group-focus-within:text-blue-500 transition-colors" />
<input
type="password"
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all"
className="w-full pl-12 pr-4 py-4 bg-[var(--background)] border border-[var(--border)] rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-[var(--surface)] outline-none transition-all"
/>
</div>
</div>
@@ -239,15 +239,15 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose, onSwitc
<div className="relative my-6 flex items-center justify-center">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-200"></div>
<div className="w-full border-t border-[var(--border)]"></div>
</div>
<span className="relative px-3 bg-white text-xs font-bold text-gray-400 uppercase">Hoặc</span>
<span className="relative px-3 bg-[var(--surface)] text-xs font-bold text-[var(--text-muted)] uppercase">Hoặc</span>
</div>
<div id="google-signin-btn-login" className="w-full flex justify-center"></div>
<div className="mt-10 pt-8 border-t border-gray-100 text-center">
<p className="text-gray-500">
<div className="mt-10 pt-8 border-t border-[var(--border)] text-center">
<p className="text-[var(--text-secondary)]">
Chưa tài khoản?{' '}
<button
onClick={() => { onClose(); onSwitchToSignup?.(); }}
@@ -39,13 +39,13 @@ export const NotificationModal: React.FC<NotificationModalProps> = ({
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm animate-in fade-in duration-200" onClick={onConfirm} />
{/* Modal Content */}
<div className="relative w-full max-w-sm bg-white rounded-[32px] shadow-2xl overflow-hidden p-8 text-center animate-in zoom-in-95 duration-200">
<div className="relative w-full max-w-sm bg-[var(--surface)] rounded-[32px] shadow-2xl overflow-hidden p-8 text-center animate-in zoom-in-95 duration-200">
<div className="flex justify-center mb-5">
{icons[type]}
</div>
<h2 className="text-xl font-black text-gray-900 mb-2">{title}</h2>
<p className="text-gray-500 text-sm leading-relaxed mb-8">
<h2 className="text-xl font-black text-[var(--text-primary)] mb-2">{title}</h2>
<p className="text-[var(--text-muted)] text-sm leading-relaxed mb-8">
{message || "Bạn không được phép gỡ bỏ thành viên này!"}
</p>