Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/Pages/Events/CalendarView.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
PinIcon,
ClockIcon,
XIcon,
PlusIcon
PlusIcon,
ScheduleIcon,
} from './EventIcons';

const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
Expand Down Expand Up @@ -71,11 +72,27 @@ function formatTime(timeStr) {
});
}

function isScheduledEvent(event) {
if (event.status !== 'draft' || !event.publish_date) return false;
const publishDate = new Date(event.publish_date);
return !isNaN(publishDate) && publishDate > new Date();
}

function pillColors(event, isAdminView) {
const status = event.status || 'draft';
const visibility = event.visibility || 'public';

if (isAdminView) {
if (isScheduledEvent(event)) {
return {
bg: 'bg-blue-500/12',
text: 'text-blue-200',
border: 'border-blue-400/40',
dot: 'bg-blue-300',
accent: 'text-blue-300',
};
}

if (status === 'draft') {
return {
bg: 'bg-amber-500/12',
Expand Down Expand Up @@ -149,6 +166,7 @@ function getBadgeText(event, isAdminView) {
const visibility = event.visibility || 'public';

if (isAdminView) {
if (isScheduledEvent(event)) return 'Scheduled';
if (status === 'draft') return 'Draft';
if (status === 'closed') return 'Closed';
if (visibility === 'private') return 'Private';
Expand Down Expand Up @@ -532,6 +550,21 @@ function EventPopup({ event, onClose, isAdminView, user }) {
{waitlistMessage && (
<p className="text-xs text-center text-emerald-300">{waitlistMessage}</p>
)}

{isAdminView && event.publish_date && (
<div className="flex items-start gap-2.5 text-sm text-slate-200">
<span className="mt-0.5 text-slate-400"><ScheduleIcon /></span>
<span>
Publish date: {new Date(event.publish_date).toLocaleString()}
</span>
</div>
)}

{isAdminView && event.published_at && (
<div className="text-xs text-slate-400">
Published at: {new Date(event.published_at).toLocaleString()}
</div>
)}
</div>
</div>
</div>
Expand Down Expand Up @@ -935,6 +968,7 @@ export default function CalendarView({
{isAdminView ? (
<div className="flex flex-wrap items-center px-5 py-3 border-t gap-x-4 gap-y-1 border-slate-700/70 bg-slate-900/35">
{[
{ label: 'Scheduled', dot: 'bg-blue-300' },
{ label: 'Published', dot: 'bg-cyan-300' },
{ label: 'Private', dot: 'bg-violet-300' },
{ label: 'Draft', dot: 'bg-amber-300' },
Expand Down
20 changes: 20 additions & 0 deletions src/Pages/Events/CreateEventPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default function CreateEventPage() {
const [maxAttendees, setMaxAttendees] = useState(UNLIMITED_ATTENDEES);
const [waitlistEnabled, setWaitlistEnabled] = useState(false);
const [waitlistSize, setWaitlistSize] = useState(10);
const [publishDate, setPublishDate] = useState('');
const {
questions,
addQuestion,
Expand Down Expand Up @@ -164,6 +165,12 @@ export default function CreateEventPage() {
debounceRef.current = setTimeout(() => performAdminSearch(query), 300);
}

function toPublishDateValue(status, publishDate) {
if (status === 'closed') return null;
if (!publishDate) return null;
return new Date(publishDate).toISOString();
}

async function handleCreateEvent() {
setSubmitError('');
if (!eventName.trim()) {
Expand All @@ -190,6 +197,16 @@ export default function CreateEventPage() {
setSubmitError('Please enter a valid waitlist size.');
return;
}
if (status === 'closed' && publishDate) {
setSubmitError('Closed events cannot have a publish date.');
return;
}
if (status === 'published' && publishDate) {
const confirmed = window.confirm(
'This event is marked published and also has a publish date. It may publish immediately if that date is in the past. Continue?'
);
if (!confirmed) return;
}

const payload = {
id: eventId,
Expand All @@ -209,6 +226,7 @@ export default function CreateEventPage() {
minimum_visible_role: visibility === 'private' ? minimumVisibleRole : '',
waitlist_enabled: waitlistEnabled,
waitlist_size: waitlistEnabled ? Number(waitlistSize) : 0,
publish_date: toPublishDateValue(status, publishDate),
};

setSubmitting(true);
Expand Down Expand Up @@ -280,6 +298,8 @@ export default function CreateEventPage() {
setWaitlistEnabled,
waitlistSize,
setWaitlistSize,
publishDate,
setPublishDate,
}}
questionActions={{
questions,
Expand Down
23 changes: 23 additions & 0 deletions src/Pages/Events/EditEventPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default function EditEventPage() {
const [waitlistSize, setWaitlistSize] = useState(10);
const [eventAdmins, setEventAdmins] = useState([]);
const [allOrgAdminsCanEdit, setAllOrgAdminsCanEdit] = useState(false);
const [publishDate, setPublishDate] = useState('');
const [adminSearch, setAdminSearch] = useState('');
const [adminSearchResults, setAdminSearchResults] = useState([]);
const [adminSearchError, setAdminSearchError] = useState('');
Expand Down Expand Up @@ -104,6 +105,7 @@ export default function EditEventPage() {
setWaitlistSize(
typeof evt.waitlist_size === 'number' && evt.waitlist_size > 0 ? evt.waitlist_size : 10,
);
setPublishDate(evt.publish_date ? evt.publish_date.slice(0, 16) : '');
setQuestions(evt.registration_form || []);
const adminIds = Array.isArray(evt.admins) ? evt.admins.map(String) : [];
setAllOrgAdminsCanEdit(!!evt.all_org_admins_can_edit);
Expand Down Expand Up @@ -175,6 +177,12 @@ export default function EditEventPage() {
debounceRef.current = setTimeout(() => performAdminSearch(query), 300);
}

function toPublishDateValue(status, publishDate) {
if (status === 'closed') return null;
if (!publishDate) return null;
return new Date(publishDate).toISOString();
}

async function handleUpdateEvent() {
setSubmitError('');
if (!eventName.trim()) {
Expand Down Expand Up @@ -204,6 +212,18 @@ export default function EditEventPage() {
}
}

if (status === 'closed' && publishDate) {
setSubmitError('Closed events cannot have a publish date.');
return;
}

if (status === 'published' && publishDate) {
const confirmed = window.confirm(
'This event is marked published and also has a publish date. It may publish immediately if that date is in the past. Continue?'
);
if (!confirmed) return;
}

const payload = {
name: eventName.trim(),
date,
Expand All @@ -220,6 +240,7 @@ export default function EditEventPage() {
waitlist_size: waitlistEnabled ? Number(waitlistSize) : 0,
admins: allOrgAdminsCanEdit ? [] : eventAdminIds,
all_org_admins_can_edit: allOrgAdminsCanEdit,
publish_date: toPublishDateValue(status, publishDate),
};

setSubmitting(true);
Expand Down Expand Up @@ -331,6 +352,8 @@ export default function EditEventPage() {
setWaitlistEnabled,
waitlistSize,
setWaitlistSize,
publishDate,
setPublishDate,
}}
questionActions={{
questions,
Expand Down
29 changes: 28 additions & 1 deletion src/Pages/Events/EventEditorForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export default function EventEditorForm({
setWaitlistEnabled,
waitlistSize,
setWaitlistSize,
publishDate,
setPublishDate,
} = form;

const {
Expand Down Expand Up @@ -243,7 +245,14 @@ export default function EventEditorForm({
<select
className="select select-bordered w-full"
value={status}
onChange={(e) => setStatus(e.target.value)}
onChange={(e) => {
const nextStatus = e.target.value;
setStatus(nextStatus);

if (nextStatus === 'closed') {
setPublishDate('');
}
}}
>
<option value="draft">Draft</option>
<option value="published">Published</option>
Expand All @@ -270,6 +279,24 @@ export default function EventEditorForm({
<option value="private">Private</option>
</select>
</label>

<label className="form-control w-full">
<div className="label">
<span className="label-text">Publish date</span>
</div>
<input
type="datetime-local"
className="input input-bordered w-full"
value={publishDate}
onChange={(e) => setPublishDate(e.target.value)}
disabled={status === 'closed'}
/>
<div className="label">
<span className="label-text-alt text-gray-500 dark:text-gray-400">
Leave blank to publish immediately when status is set to published. Set a future date while status is draft to schedule auto-publish.
</span>
</div>
</label>
</div>

{visibility === 'private' && (
Expand Down
10 changes: 10 additions & 0 deletions src/Pages/Events/EventIcons.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,13 @@ export function EditIcon() {
</svg>
);
}

export function ScheduleIcon() {
return (
<svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M8 2v4M16 2v4M3 10h18" />
<rect x="3" y="4" width="18" height="17" rx="2" />
<path d="M12 12v4l3 2" />
</svg>
);
}
Loading