392 lines
12 KiB
JavaScript
392 lines
12 KiB
JavaScript
import React, { useState } from 'react';
|
|
|
|
const mockEvents = [
|
|
{
|
|
id: 1,
|
|
title: 'Monthly Community Townhall',
|
|
description: 'Join us for our monthly Q&A session with the AeThex team. Ask questions, get updates, and connect with the community.',
|
|
type: 'stage',
|
|
channel: 'community-stage',
|
|
startTime: new Date(Date.now() + 2 * 60 * 60 * 1000), // 2 hours from now
|
|
endTime: new Date(Date.now() + 4 * 60 * 60 * 1000),
|
|
host: { name: 'Anderson', avatar: 'A', color: '#ff0000' },
|
|
interested: 234,
|
|
image: null,
|
|
recurring: 'monthly',
|
|
},
|
|
{
|
|
id: 2,
|
|
title: 'Game Night: Among Us',
|
|
description: 'Weekly game night! This week we\'re playing Among Us. Join the voice channel 10 minutes early.',
|
|
type: 'voice',
|
|
channel: 'gaming-voice',
|
|
startTime: new Date(Date.now() + 26 * 60 * 60 * 1000), // Tomorrow
|
|
endTime: new Date(Date.now() + 29 * 60 * 60 * 1000),
|
|
host: { name: 'Sarah', avatar: 'S', color: '#ffa500' },
|
|
interested: 89,
|
|
image: null,
|
|
recurring: 'weekly',
|
|
},
|
|
{
|
|
id: 3,
|
|
title: 'AeThex SDK Workshop',
|
|
description: 'Learn how to integrate the AeThex SDK into your projects. Hands-on coding session with live Q&A.',
|
|
type: 'external',
|
|
location: 'https://workshop.aethex.com',
|
|
startTime: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), // 3 days
|
|
endTime: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000 + 2 * 60 * 60 * 1000),
|
|
host: { name: 'Trevor', avatar: 'T', color: '#0066ff' },
|
|
interested: 456,
|
|
image: null,
|
|
recurring: null,
|
|
},
|
|
];
|
|
|
|
export default function EventsPanel({ onClose, onCreateEvent }) {
|
|
const [activeTab, setActiveTab] = useState('upcoming');
|
|
const [selectedEvent, setSelectedEvent] = useState(null);
|
|
|
|
const formatDate = (date) => {
|
|
const now = new Date();
|
|
const diff = date - now;
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
|
|
if (days === 0) {
|
|
const hours = Math.floor(diff / (1000 * 60 * 60));
|
|
if (hours === 0) {
|
|
const minutes = Math.floor(diff / (1000 * 60));
|
|
return `In ${minutes} minutes`;
|
|
}
|
|
return `In ${hours} hours`;
|
|
} else if (days === 1) {
|
|
return 'Tomorrow';
|
|
} else {
|
|
return date.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' });
|
|
}
|
|
};
|
|
|
|
const formatTime = (date) => {
|
|
return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
|
|
};
|
|
|
|
const getTypeIcon = (type) => {
|
|
switch (type) {
|
|
case 'stage': return '📢';
|
|
case 'voice': return '🔊';
|
|
case 'external': return '🔗';
|
|
default: return '📅';
|
|
}
|
|
};
|
|
|
|
if (selectedEvent) {
|
|
return (
|
|
<EventDetail
|
|
event={selectedEvent}
|
|
onBack={() => setSelectedEvent(null)}
|
|
formatDate={formatDate}
|
|
formatTime={formatTime}
|
|
getTypeIcon={getTypeIcon}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="events-panel">
|
|
<div className="events-header">
|
|
<h2>Events</h2>
|
|
<div className="events-actions">
|
|
<button className="create-event-btn" onClick={onCreateEvent}>
|
|
+ Create Event
|
|
</button>
|
|
<button className="events-close" onClick={onClose}>✕</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="events-tabs">
|
|
<button
|
|
className={`events-tab ${activeTab === 'upcoming' ? 'active' : ''}`}
|
|
onClick={() => setActiveTab('upcoming')}
|
|
>
|
|
Upcoming
|
|
</button>
|
|
<button
|
|
className={`events-tab ${activeTab === 'recurring' ? 'active' : ''}`}
|
|
onClick={() => setActiveTab('recurring')}
|
|
>
|
|
Recurring
|
|
</button>
|
|
<button
|
|
className={`events-tab ${activeTab === 'past' ? 'active' : ''}`}
|
|
onClick={() => setActiveTab('past')}
|
|
>
|
|
Past
|
|
</button>
|
|
</div>
|
|
|
|
<div className="events-list">
|
|
{mockEvents.map(event => (
|
|
<div
|
|
key={event.id}
|
|
className="event-card"
|
|
onClick={() => setSelectedEvent(event)}
|
|
>
|
|
<div className="event-date-badge">
|
|
<span className="event-month">
|
|
{event.startTime.toLocaleDateString('en-US', { month: 'short' })}
|
|
</span>
|
|
<span className="event-day">
|
|
{event.startTime.getDate()}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="event-info">
|
|
<div className="event-timing">
|
|
<span className="event-time-label">{formatDate(event.startTime)}</span>
|
|
<span className="event-time">
|
|
{formatTime(event.startTime)} - {formatTime(event.endTime)}
|
|
</span>
|
|
</div>
|
|
|
|
<h3 className="event-title">
|
|
<span className="event-type-icon">{getTypeIcon(event.type)}</span>
|
|
{event.title}
|
|
</h3>
|
|
|
|
<p className="event-description">{event.description}</p>
|
|
|
|
<div className="event-meta">
|
|
<div className="event-host">
|
|
<div
|
|
className="host-avatar"
|
|
style={event.host.color ? { background: event.host.color } : undefined}
|
|
>
|
|
{event.host.avatar}
|
|
</div>
|
|
<span>Hosted by {event.host.name}</span>
|
|
</div>
|
|
|
|
<div className="event-interested">
|
|
<span className="interested-count">⭐ {event.interested} interested</span>
|
|
</div>
|
|
</div>
|
|
|
|
{event.recurring && (
|
|
<div className="recurring-badge">
|
|
🔄 Repeats {event.recurring}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{mockEvents.length === 0 && (
|
|
<div className="events-empty">
|
|
<span className="empty-icon">📅</span>
|
|
<h3>No upcoming events</h3>
|
|
<p>Create an event to bring your community together!</p>
|
|
<button className="create-event-btn" onClick={onCreateEvent}>
|
|
Create Event
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function EventDetail({ event, onBack, formatDate, formatTime, getTypeIcon }) {
|
|
const [isInterested, setIsInterested] = useState(false);
|
|
|
|
return (
|
|
<div className="event-detail">
|
|
<button className="back-btn" onClick={onBack}>
|
|
← Back to Events
|
|
</button>
|
|
|
|
<div className="event-detail-header">
|
|
<div className="event-type-badge">
|
|
{getTypeIcon(event.type)} {event.type.charAt(0).toUpperCase() + event.type.slice(1)}
|
|
</div>
|
|
<h1>{event.title}</h1>
|
|
</div>
|
|
|
|
<div className="event-detail-content">
|
|
<div className="detail-section">
|
|
<h4>📅 Date & Time</h4>
|
|
<p>{formatDate(event.startTime)}</p>
|
|
<p className="time-range">
|
|
{formatTime(event.startTime)} - {formatTime(event.endTime)}
|
|
</p>
|
|
{event.recurring && (
|
|
<p className="recurring-info">🔄 Repeats {event.recurring}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="detail-section">
|
|
<h4>📍 Location</h4>
|
|
{event.type === 'external' ? (
|
|
<a href={event.location} className="external-link">
|
|
{event.location}
|
|
</a>
|
|
) : (
|
|
<p>#{event.channel}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="detail-section">
|
|
<h4>📝 Description</h4>
|
|
<p>{event.description}</p>
|
|
</div>
|
|
|
|
<div className="detail-section">
|
|
<h4>👤 Host</h4>
|
|
<div className="host-info">
|
|
<div
|
|
className="host-avatar large"
|
|
style={event.host.color ? { background: event.host.color } : undefined}
|
|
>
|
|
{event.host.avatar}
|
|
</div>
|
|
<span>{event.host.name}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="interested-section">
|
|
<span>⭐ {event.interested + (isInterested ? 1 : 0)} interested</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="event-detail-actions">
|
|
<button
|
|
className={`interested-btn ${isInterested ? 'active' : ''}`}
|
|
onClick={() => setIsInterested(!isInterested)}
|
|
>
|
|
{isInterested ? '⭐ Interested' : '☆ Mark as Interested'}
|
|
</button>
|
|
<button className="share-event-btn">
|
|
📤 Share
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Event creation modal
|
|
export function CreateEventModal({ onSubmit, onClose }) {
|
|
const [title, setTitle] = useState('');
|
|
const [description, setDescription] = useState('');
|
|
const [type, setType] = useState('voice');
|
|
const [date, setDate] = useState('');
|
|
const [startTime, setStartTime] = useState('');
|
|
const [endTime, setEndTime] = useState('');
|
|
|
|
const handleSubmit = () => {
|
|
if (!title.trim() || !date || !startTime) return;
|
|
|
|
onSubmit?.({
|
|
title,
|
|
description,
|
|
type,
|
|
date,
|
|
startTime,
|
|
endTime,
|
|
});
|
|
onClose?.();
|
|
};
|
|
|
|
return (
|
|
<div className="modal-overlay" onClick={onClose}>
|
|
<div className="create-event-modal" onClick={(e) => e.stopPropagation()}>
|
|
<div className="modal-header">
|
|
<h2>Create Event</h2>
|
|
<button className="modal-close" onClick={onClose}>✕</button>
|
|
</div>
|
|
|
|
<div className="modal-content">
|
|
<div className="form-group">
|
|
<label>Event Type</label>
|
|
<div className="event-type-options">
|
|
<button
|
|
className={`type-option ${type === 'stage' ? 'active' : ''}`}
|
|
onClick={() => setType('stage')}
|
|
>
|
|
📢 Stage
|
|
</button>
|
|
<button
|
|
className={`type-option ${type === 'voice' ? 'active' : ''}`}
|
|
onClick={() => setType('voice')}
|
|
>
|
|
🔊 Voice
|
|
</button>
|
|
<button
|
|
className={`type-option ${type === 'external' ? 'active' : ''}`}
|
|
onClick={() => setType('external')}
|
|
>
|
|
🔗 External
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>Event Title</label>
|
|
<input
|
|
type="text"
|
|
placeholder="What's your event called?"
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
maxLength={100}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>Description</label>
|
|
<textarea
|
|
placeholder="Tell people what this event is about..."
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-row">
|
|
<div className="form-group">
|
|
<label>Date</label>
|
|
<input
|
|
type="date"
|
|
value={date}
|
|
onChange={(e) => setDate(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="form-group">
|
|
<label>Start Time</label>
|
|
<input
|
|
type="time"
|
|
value={startTime}
|
|
onChange={(e) => setStartTime(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="form-group">
|
|
<label>End Time</label>
|
|
<input
|
|
type="time"
|
|
value={endTime}
|
|
onChange={(e) => setEndTime(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="modal-footer">
|
|
<button className="cancel-btn" onClick={onClose}>Cancel</button>
|
|
<button
|
|
className="submit-btn"
|
|
onClick={handleSubmit}
|
|
disabled={!title.trim() || !date || !startTime}
|
|
>
|
|
Create Event
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|