Pinnacle/src/components/TimelineView.tsx

727 lines
31 KiB
TypeScript
Raw Normal View History

2026-08-03 20:24:04 +00:00
import React, { useState } from 'react';
import {
Clock,
Plus,
FileText,
Calendar,
Users,
Award,
Gavel,
ShieldAlert,
Search,
Filter,
Paperclip,
MapPin,
User,
Edit2,
Trash2,
Download,
X,
CheckCircle,
FileCode,
Tag,
ArrowUpDown,
Sparkles,
ExternalLink
} from 'lucide-react';
import { ProjectStore, TimelineEvent, TimelineEventType, UserRole, Attachment } from '../types';
import { AttachmentUploader } from './AttachmentUploader';
interface TimelineViewProps {
store: ProjectStore;
onUpdateStore: (newStore: ProjectStore) => void;
userRole?: UserRole;
currentUserName?: string;
}
export const TimelineView: React.FC<TimelineViewProps> = ({
store,
onUpdateStore,
userRole = 'viewer',
currentUserName = 'System User'
}) => {
const events = store.timelineEvents || [];
const canEdit = userRole === 'admin' || userRole === 'editor';
// Search & Filter State
const [searchQuery, setSearchQuery] = useState('');
const [typeFilter, setTypeFilter] = useState<string>('all');
const [sortOrder, setSortOrder] = useState<'desc' | 'asc'>('desc');
// Modal State
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingEvent, setEditingEvent] = useState<Partial<TimelineEvent> | null>(null);
// Modal Form Inputs
const [title, setTitle] = useState('');
const [eventType, setEventType] = useState<TimelineEventType>('circular');
const [category, setCategory] = useState('');
const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
const [time, setTime] = useState('10:00');
const [referenceNumber, setReferenceNumber] = useState('');
const [location, setLocation] = useState('');
const [author, setAuthor] = useState(currentUserName);
const [description, setDescription] = useState('');
const [attachments, setAttachments] = useState<Attachment[]>([]);
// Open modal to create a new event
const handleOpenCreateModal = () => {
setEditingEvent(null);
setTitle('');
setEventType('circular');
setCategory('Official Circular');
setDate(new Date().toISOString().split('T')[0]);
setTime('10:00');
setReferenceNumber(`PIN-${new Date().getFullYear()}-${Math.floor(100 + Math.random() * 900)}`);
setLocation('Executive Office');
setAuthor(currentUserName);
setDescription('');
setAttachments([]);
setIsModalOpen(true);
};
// Open modal to edit existing event
const handleOpenEditModal = (event: TimelineEvent) => {
setEditingEvent(event);
setTitle(event.title);
setEventType(event.type);
setCategory(event.category || '');
setDate(event.date);
setTime(event.time || '');
setReferenceNumber(event.referenceNumber || '');
setLocation(event.location || '');
setAuthor(event.author || currentUserName);
setDescription(event.description);
setAttachments(event.attachments || []);
setIsModalOpen(true);
};
// Save event (Create or Update)
const handleSaveEvent = (e: React.FormEvent) => {
e.preventDefault();
if (!title.trim() || !date) return;
let updatedEvents = [...events];
if (editingEvent && editingEvent.id) {
// Update existing
updatedEvents = updatedEvents.map((evt) =>
evt.id === editingEvent.id
? {
...evt,
title: title.trim(),
type: eventType,
category: category.trim() || undefined,
date,
time: time.trim() || undefined,
referenceNumber: referenceNumber.trim() || undefined,
location: location.trim() || undefined,
author: author.trim() || currentUserName,
description: description.trim(),
attachments
}
: evt
);
} else {
// Create new
const nextId = (store.ids.timelineEvent || events.length + 1) + 1;
const newEvt: TimelineEvent = {
id: nextId,
title: title.trim(),
type: eventType,
category: category.trim() || getTypeLabel(eventType),
date,
time: time.trim() || undefined,
referenceNumber: referenceNumber.trim() || undefined,
location: location.trim() || undefined,
author: author.trim() || currentUserName,
description: description.trim(),
attachments,
createdAt: new Date().toISOString()
};
updatedEvents.push(newEvt);
}
onUpdateStore({
...store,
timelineEvents: updatedEvents,
ids: {
...store.ids,
timelineEvent: (store.ids.timelineEvent || events.length + 1) + 1
},
logs: [
{
id: store.ids.log || Date.now(),
action: editingEvent ? 'TIMELINE_EVENT_UPDATED' : 'TIMELINE_EVENT_CREATED',
type: 'timeline',
details: `${editingEvent ? 'Updated' : 'Created'} timeline event: ${title.trim()} (${referenceNumber || eventType})`,
user: currentUserName,
timestamp: new Date().toISOString()
},
...store.logs
]
});
setIsModalOpen(false);
};
// Delete Event
const handleDeleteEvent = (id: number) => {
if (!window.confirm('Are you sure you want to delete this timeline event?')) return;
const updatedEvents = events.filter((e) => e.id !== id);
onUpdateStore({
...store,
timelineEvents: updatedEvents,
logs: [
{
id: store.ids.log || Date.now(),
action: 'TIMELINE_EVENT_DELETED',
type: 'timeline',
details: `Deleted timeline event ID #${id}`,
user: currentUserName,
timestamp: new Date().toISOString()
},
...store.logs
]
});
};
// Filter & Sort Events
const filteredEvents = events
.filter((e) => {
if (typeFilter !== 'all' && e.type !== typeFilter) return false;
if (searchQuery.trim()) {
const q = searchQuery.toLowerCase();
return (
e.title.toLowerCase().includes(q) ||
e.description.toLowerCase().includes(q) ||
(e.referenceNumber && e.referenceNumber.toLowerCase().includes(q)) ||
(e.category && e.category.toLowerCase().includes(q)) ||
(e.author && e.author.toLowerCase().includes(q))
);
}
return true;
})
.sort((a, b) => {
const dateA = new Date(`${a.date}T${a.time || '00:00'}`).getTime();
const dateB = new Date(`${b.date}T${b.time || '00:00'}`).getTime();
return sortOrder === 'desc' ? dateB - dateA : dateA - dateB;
});
// Helpers for Type Colors & Icons
function getTypeBadge(type: TimelineEventType) {
switch (type) {
case 'circular':
return 'bg-cyan-500/10 text-cyan-400 border-cyan-500/30';
case 'meeting':
return 'bg-amber-500/10 text-amber-400 border-amber-500/30';
case 'milestone':
return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30';
case 'decision':
return 'bg-purple-500/10 text-purple-400 border-purple-500/30';
case 'release':
case 'audit':
return 'bg-rose-500/10 text-rose-400 border-rose-500/30';
default:
return 'bg-slate-800 text-slate-300 border-slate-700';
}
}
function getTypeIcon(type: TimelineEventType) {
switch (type) {
case 'circular':
return <FileText className="w-4 h-4 text-cyan-400" />;
case 'meeting':
return <Users className="w-4 h-4 text-amber-400" />;
case 'milestone':
return <Award className="w-4 h-4 text-emerald-400" />;
case 'decision':
return <Gavel className="w-4 h-4 text-purple-400" />;
case 'release':
case 'audit':
return <ShieldAlert className="w-4 h-4 text-rose-400" />;
default:
return <Clock className="w-4 h-4 text-slate-400" />;
}
}
function getTypeLabel(type: TimelineEventType) {
switch (type) {
case 'circular':
return 'Circular Published';
case 'meeting':
return 'Committee Meeting';
case 'milestone':
return 'Milestone Reached';
case 'decision':
return 'Key Decision';
case 'release':
return 'Official Release';
case 'audit':
return 'Audit Review';
default:
return 'Custom Event';
}
}
// Count Statistics
const circularsCount = events.filter((e) => e.type === 'circular').length;
const meetingsCount = events.filter((e) => e.type === 'meeting').length;
const milestonesCount = events.filter((e) => e.type === 'milestone').length;
const docsCount = events.reduce((acc, e) => acc + (e.attachments ? e.attachments.length : 0), 0);
return (
<div className="space-y-6">
{/* Page Header */}
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 bg-slate-900 border border-slate-800/80 p-6 rounded-3xl shadow-xl">
<div className="space-y-1">
<div className="flex items-center space-x-3">
<div className="p-2.5 bg-amber-500/10 border border-amber-500/30 rounded-2xl text-amber-400">
<Clock className="w-6 h-6" />
</div>
<div>
<h1 className="text-xl font-bold text-slate-100">Interactive Timeline & Circular Registry</h1>
<p className="text-xs text-slate-400">
Chronological record of official meetings, circulars published, milestone achievements, and executive directives.
</p>
</div>
</div>
</div>
{canEdit && (
<button
onClick={handleOpenCreateModal}
className="px-4 py-2.5 bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold rounded-xl text-xs flex items-center justify-center space-x-2 shadow-lg transition cursor-pointer self-start md:self-auto shrink-0"
>
<Plus className="w-4 h-4" />
<span>Create Timeline Event</span>
</button>
)}
</div>
{/* Summary Analytics Cards */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl space-y-1">
<div className="flex items-center justify-between text-xs text-slate-400">
<span>Total Timeline Events</span>
<Clock className="w-4 h-4 text-amber-400" />
</div>
<div className="text-2xl font-extrabold text-slate-100">{events.length}</div>
<div className="text-[10px] text-slate-500">Historical & scheduled entries</div>
</div>
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl space-y-1">
<div className="flex items-center justify-between text-xs text-slate-400">
<span>Circulars Issued</span>
<FileText className="w-4 h-4 text-cyan-400" />
</div>
<div className="text-2xl font-extrabold text-cyan-400">{circularsCount}</div>
<div className="text-[10px] text-slate-500">Official governance publications</div>
</div>
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl space-y-1">
<div className="flex items-center justify-between text-xs text-slate-400">
<span>Committee Meetings</span>
<Users className="w-4 h-4 text-amber-400" />
</div>
<div className="text-2xl font-extrabold text-amber-400">{meetingsCount}</div>
<div className="text-[10px] text-slate-500">PMT & PSC sessions logged</div>
</div>
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl space-y-1">
<div className="flex items-center justify-between text-xs text-slate-400">
<span>Supporting Documents</span>
<Paperclip className="w-4 h-4 text-emerald-400" />
</div>
<div className="text-2xl font-extrabold text-emerald-400">{docsCount}</div>
<div className="text-[10px] text-slate-500">Attached files & circular PDFs</div>
</div>
</div>
{/* Filter and Search Bar */}
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl space-y-3">
<div className="flex flex-col md:flex-row gap-3 items-center justify-between">
{/* Search Box */}
<div className="relative w-full md:w-80">
<Search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-500" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search timeline, circular ref, title..."
className="w-full bg-slate-950 border border-slate-800 rounded-xl pl-9 pr-3 py-2 text-xs text-slate-200 focus:outline-none focus:border-amber-500 transition"
/>
{searchQuery && (
<button
onClick={() => setSearchQuery('')}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-300"
>
<X className="w-3.5 h-3.5" />
</button>
)}
</div>
{/* Type Filter Tabs */}
<div className="flex items-center space-x-1 overflow-x-auto w-full md:w-auto pb-1 md:pb-0 text-xs">
{[
{ id: 'all', label: 'All Events' },
{ id: 'circular', label: 'Circulars' },
{ id: 'meeting', label: 'Meetings' },
{ id: 'milestone', label: 'Milestones' },
{ id: 'decision', label: 'Decisions' }
].map((tab) => (
<button
key={tab.id}
onClick={() => setTypeFilter(tab.id)}
className={`px-3 py-1.5 rounded-lg font-medium whitespace-nowrap transition cursor-pointer ${
typeFilter === tab.id
? 'bg-amber-500 text-slate-950 font-bold shadow'
: 'bg-slate-950 text-slate-400 hover:bg-slate-800 hover:text-slate-200 border border-slate-800'
}`}
>
{tab.label}
</button>
))}
{/* Sort Toggle */}
<button
onClick={() => setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc')}
className="px-3 py-1.5 bg-slate-950 hover:bg-slate-800 text-slate-300 rounded-lg border border-slate-800 flex items-center space-x-1 font-medium transition cursor-pointer ml-auto"
title="Toggle Date Sort Order"
>
<ArrowUpDown className="w-3.5 h-3.5 text-amber-400" />
<span className="text-[11px]">{sortOrder === 'desc' ? 'Newest First' : 'Oldest First'}</span>
</button>
</div>
</div>
</div>
{/* Vertical Interactive Timeline */}
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 shadow-xl relative">
{filteredEvents.length === 0 ? (
<div className="py-16 text-center space-y-3">
<div className="w-12 h-12 rounded-2xl bg-slate-800 text-slate-500 mx-auto flex items-center justify-center">
<Clock className="w-6 h-6" />
</div>
<div className="text-sm font-semibold text-slate-300">No timeline events match your search criteria</div>
<p className="text-xs text-slate-500 max-w-sm mx-auto">
Try adjusting your search query, selecting "All Events", or creating a new timeline entry.
</p>
{canEdit && (
<button
onClick={handleOpenCreateModal}
className="px-4 py-2 bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold rounded-xl text-xs inline-flex items-center space-x-1.5 shadow mt-2 cursor-pointer"
>
<Plus className="w-4 h-4" />
<span>Create Timeline Event</span>
</button>
)}
</div>
) : (
<div className="relative pl-6 md:pl-8 border-l-2 border-slate-800 space-y-8 my-2">
{filteredEvents.map((evt) => (
<div key={evt.id} className="relative group">
{/* Pulsing Timeline Node */}
<div
className={`absolute -left-[31px] md:-left-[39px] top-1.5 w-6 h-6 rounded-full border-2 flex items-center justify-center shadow-lg transition-transform group-hover:scale-110 ${
evt.type === 'circular'
? 'bg-slate-900 border-cyan-400 text-cyan-400 shadow-cyan-500/20'
: evt.type === 'meeting'
? 'bg-slate-900 border-amber-400 text-amber-400 shadow-amber-500/20'
: evt.type === 'milestone'
? 'bg-slate-900 border-emerald-400 text-emerald-400 shadow-emerald-500/20'
: evt.type === 'decision'
? 'bg-slate-900 border-purple-400 text-purple-400 shadow-purple-500/20'
: 'bg-slate-900 border-rose-400 text-rose-400 shadow-rose-500/20'
}`}
>
<div className="w-2 h-2 rounded-full bg-current" />
</div>
{/* Event Card */}
<div className="bg-slate-950/80 border border-slate-800/90 rounded-2xl p-5 hover:border-slate-700 transition shadow-md space-y-3">
<div className="flex flex-wrap items-start justify-between gap-2 border-b border-slate-800/60 pb-3">
<div className="space-y-1">
<div className="flex flex-wrap items-center gap-2">
{/* Type Badge */}
<span
className={`px-2.5 py-0.5 rounded-lg text-[10px] font-bold border inline-flex items-center space-x-1 ${getTypeBadge(
evt.type
)}`}
>
{getTypeIcon(evt.type)}
<span>{evt.category || getTypeLabel(evt.type)}</span>
</span>
{/* Reference Number if present */}
{evt.referenceNumber && (
<span className="px-2 py-0.5 bg-slate-800 text-amber-300 font-mono text-[10px] rounded border border-slate-700 font-bold inline-flex items-center space-x-1">
<Tag className="w-3 h-3 text-amber-400" />
<span>Ref: {evt.referenceNumber}</span>
</span>
)}
</div>
<h3 className="text-base font-bold text-slate-100 group-hover:text-amber-400 transition">
{evt.title}
</h3>
</div>
{/* Controls for Admin / Editor */}
<div className="flex items-center space-x-1 opacity-90 sm:opacity-0 group-hover:opacity-100 transition">
{canEdit && (
<>
<button
onClick={() => handleOpenEditModal(evt)}
className="p-1.5 text-slate-400 hover:text-amber-400 hover:bg-slate-800 rounded-lg transition"
title="Edit Event"
>
<Edit2 className="w-4 h-4" />
</button>
<button
onClick={() => handleDeleteEvent(evt.id)}
className="p-1.5 text-slate-400 hover:text-rose-400 hover:bg-slate-800 rounded-lg transition"
title="Delete Event"
>
<Trash2 className="w-4 h-4" />
</button>
</>
)}
</div>
</div>
{/* Metadata Bar */}
<div className="flex flex-wrap items-center gap-y-2 gap-x-4 text-xs text-slate-400">
<div className="flex items-center space-x-1.5 text-slate-300 font-medium">
<Calendar className="w-3.5 h-3.5 text-amber-400" />
<span>{evt.date}</span>
{evt.time && <span className="text-slate-500">at {evt.time}</span>}
</div>
{evt.location && (
<div className="flex items-center space-x-1 text-slate-400">
<MapPin className="w-3.5 h-3.5 text-slate-500" />
<span>{evt.location}</span>
</div>
)}
{evt.author && (
<div className="flex items-center space-x-1 text-slate-400">
<User className="w-3.5 h-3.5 text-slate-500" />
<span>Published by: <strong className="text-slate-300 font-normal">{evt.author}</strong></span>
</div>
)}
</div>
{/* Description Body */}
<p className="text-xs text-slate-300 leading-relaxed whitespace-pre-line">{evt.description}</p>
{/* Supporting Attachments Display */}
{evt.attachments && evt.attachments.length > 0 && (
<div className="pt-2 border-t border-slate-800/60">
<div className="text-[11px] font-bold text-slate-400 mb-2 flex items-center space-x-1.5">
<Paperclip className="w-3.5 h-3.5 text-amber-400" />
<span>Supporting Documents & Circular Files ({evt.attachments.length})</span>
</div>
<div className="flex flex-wrap gap-2">
{evt.attachments.map((att) => (
<a
key={att.id}
href={att.url}
download={att.name}
target="_blank"
rel="noopener noreferrer"
className="px-3 py-1.5 bg-slate-900 hover:bg-slate-800 border border-slate-800 hover:border-amber-500/40 text-slate-200 rounded-xl text-xs flex items-center space-x-2 transition group/file"
>
<FileText className="w-3.5 h-3.5 text-amber-400 shrink-0" />
<span className="font-medium truncate max-w-[180px]">{att.name}</span>
<span className="text-[10px] text-slate-500">
({(att.size / 1024).toFixed(0)} KB)
</span>
<Download className="w-3.5 h-3.5 text-slate-500 group-hover/file:text-amber-400 transition shrink-0 ml-1" />
</a>
))}
</div>
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* CREATE / EDIT TIMELINE EVENT MODAL */}
{isModalOpen && (
<div className="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 flex items-center justify-center p-4 overflow-y-auto">
<div className="bg-slate-900 border border-slate-800 rounded-3xl max-w-xl w-full p-6 shadow-2xl space-y-5 my-8 max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between border-b border-slate-800 pb-3">
<h2 className="text-lg font-bold text-slate-100 flex items-center space-x-2">
<Clock className="w-5 h-5 text-amber-400" />
<span>{editingEvent ? 'Edit Timeline Event' : 'Create Custom Timeline Event'}</span>
</h2>
<button
onClick={() => setIsModalOpen(false)}
className="p-1 rounded-lg text-slate-400 hover:text-white hover:bg-slate-800"
>
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSaveEvent} className="space-y-4 text-xs">
{/* Event Title */}
<div>
<label className="block text-slate-400 mb-1 font-medium">Event Title *</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="e.g. Published Circular PIN-2026-003 or Mid-Year Strategy Review"
required
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
{/* Event Type & Category */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-slate-400 mb-1 font-medium">Event Type</label>
<select
value={eventType}
onChange={(e) => {
const newT = e.target.value as TimelineEventType;
setEventType(newT);
if (!category || category === getTypeLabel(eventType)) {
setCategory(getTypeLabel(newT));
}
}}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
>
<option value="circular">Circular Published</option>
<option value="meeting">Committee Meeting</option>
<option value="milestone">Milestone Reached</option>
<option value="decision">Executive Decision</option>
<option value="release">Official Release / Launch</option>
<option value="audit">Audit & Compliance Review</option>
<option value="other">Other Event</option>
</select>
</div>
<div>
<label className="block text-slate-400 mb-1 font-medium">Category / Badge Name</label>
<input
type="text"
value={category}
onChange={(e) => setCategory(e.target.value)}
placeholder="e.g. Official Circular, Board Directive"
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
</div>
{/* Date, Time & Reference Number */}
<div className="grid grid-cols-3 gap-3">
<div>
<label className="block text-slate-400 mb-1 font-medium">Date *</label>
<input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
required
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1 font-medium">Time (Optional)</label>
<input
type="time"
value={time}
onChange={(e) => setTime(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1 font-medium">Reference Number</label>
<input
type="text"
value={referenceNumber}
onChange={(e) => setReferenceNumber(e.target.value)}
placeholder="e.g. PIN-2026-003"
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-amber-300 font-mono focus:outline-none focus:border-amber-500"
/>
</div>
</div>
{/* Location & Author */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-slate-400 mb-1 font-medium">Location / Venue</label>
<input
type="text"
value={location}
onChange={(e) => setLocation(e.target.value)}
placeholder="e.g. Main Boardroom / Portal"
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1 font-medium">Author / Published By</label>
<input
type="text"
value={author}
onChange={(e) => setAuthor(e.target.value)}
placeholder="e.g. PMO Office"
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
</div>
{/* Description */}
<div>
<label className="block text-slate-400 mb-1 font-medium">Description / Details</label>
<textarea
rows={3}
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Provide comprehensive details, agenda, rationale or context for this timeline event..."
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-2.5 text-slate-200 focus:outline-none focus:border-amber-500"
/>
</div>
{/* Attachment Uploader */}
<div className="pt-2 border-t border-slate-800">
<AttachmentUploader
attachments={attachments}
onChange={setAttachments}
label="Upload Supporting Documents (PDF, Circulars, Reports)"
/>
</div>
<div className="flex justify-end space-x-3 pt-3">
<button
type="button"
onClick={() => setIsModalOpen(false)}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded-xl font-medium cursor-pointer"
>
Cancel
</button>
<button
type="submit"
className="px-5 py-2 bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold rounded-xl shadow cursor-pointer"
>
{editingEvent ? 'Save Changes' : 'Create Event'}
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
};