diff --git a/src/Pages/Events/CreateEventPage.js b/src/Pages/Events/CreateEventPage.js
index dd87d4a92..c130510c8 100644
--- a/src/Pages/Events/CreateEventPage.js
+++ b/src/Pages/Events/CreateEventPage.js
@@ -87,6 +87,9 @@ export default function CreateEventPage() {
});
const [location, setLocation] = useState('');
const [description, setDescription] = useState('');
+ const [status, setStatus] = useState('draft');
+ const [visibility, setVisibility] = useState('public');
+ const [minimumVisibleRole, setMinimumVisibleRole] = useState('');
const [maxAttendees, setMaxAttendees] = useState(UNLIMITED_ATTENDEES);
const [questions, setQuestions] = useState(defaultQuestions);
const [submitError, setSubmitError] = useState('');
@@ -176,6 +179,10 @@ export default function CreateEventPage() {
setSubmitError('Could not resolve your user id.');
return;
}
+ if (visibility === 'private' && !minimumVisibleRole) {
+ setSubmitError('Please select a minimum visible role for private events.');
+ return;
+ }
const payload = {
id: eventId,
@@ -184,12 +191,14 @@ export default function CreateEventPage() {
time,
location: location.trim(),
description: description.trim(),
- admins: [adminId],
+ admins: [adminId], // The event creator becomes the initial event admin in SCEvents
registration_form: toApiRegistrationForm(questions),
max_attendees:
maxAttendees === UNLIMITED_ATTENDEES ? UNLIMITED_ATTENDEES : Number(maxAttendees),
created_at: new Date().toISOString(),
- status: 'draft',
+ status,
+ visibility,
+ minimum_visible_role: visibility === 'private' ? minimumVisibleRole : '',
};
setSubmitting(true);
@@ -336,6 +345,61 @@ export default function CreateEventPage() {
placeholder="No limit"
/>
+
+
+
+
+
+
+
+ {visibility === 'private' && (
+
+ )}
diff --git a/src/Pages/Events/EditEventPage.js b/src/Pages/Events/EditEventPage.js
index 08613db5f..e758a5e08 100644
--- a/src/Pages/Events/EditEventPage.js
+++ b/src/Pages/Events/EditEventPage.js
@@ -56,6 +56,9 @@ export default function EditEventPage() {
const [time, setTime] = useState('');
const [location, setLocation] = useState('');
const [description, setDescription] = useState('');
+ const [status, setStatus] = useState('draft');
+ const [visibility, setVisibility] = useState('public');
+ const [minimumVisibleRole, setMinimumVisibleRole] = useState('');
const [maxAttendees, setMaxAttendees] = useState(UNLIMITED_ATTENDEES);
const [questions, setQuestions] = useState([]);
const [eventAdmins, setEventAdmins] = useState([]);
@@ -83,7 +86,12 @@ export default function EditEventPage() {
setTime(evt.time || '');
setLocation(evt.location || '');
setDescription(evt.description || '');
- setMaxAttendees(evt.max_attendees || UNLIMITED_ATTENDEES);
+ setStatus(evt.status || 'draft');
+ setVisibility(evt.visibility || 'public');
+ setMinimumVisibleRole(evt.minimum_visible_role || '');
+ setMaxAttendees(
+ typeof evt.max_attendees === 'number' ? evt.max_attendees : UNLIMITED_ATTENDEES,
+ );
setQuestions(evt.registration_form || []);
setEventAdmins(evt.admins || []);
}
@@ -172,6 +180,11 @@ export default function EditEventPage() {
return;
}
+ if (visibility === 'private' && !minimumVisibleRole) {
+ setSubmitError('Please select a minimum visible role for private events.');
+ return;
+ }
+
const payload = {
name: eventName.trim(),
date,
@@ -181,6 +194,9 @@ export default function EditEventPage() {
registration_form: toApiRegistrationForm(questions),
max_attendees:
maxAttendees === UNLIMITED_ATTENDEES ? UNLIMITED_ATTENDEES : Number(maxAttendees),
+ status,
+ visibility,
+ minimum_visible_role: visibility === 'private' ? minimumVisibleRole : '',
};
setSubmitting(true);
@@ -249,6 +265,7 @@ export default function EditEventPage() {
);
}
+ // Edit access: only users listed in event.admins can update an event
const isEventAdmin = eventAdmins.includes(userId);
if (!isEventAdmin) {
return (
@@ -360,6 +377,61 @@ export default function EditEventPage() {
placeholder="No limit"
/>
+
+
+
+
+
+
+
+ {visibility === 'private' && (
+
+ )}
diff --git a/src/Pages/Events/Events.js b/src/Pages/Events/Events.js
index cfa3be327..26461c395 100644
--- a/src/Pages/Events/Events.js
+++ b/src/Pages/Events/Events.js
@@ -74,8 +74,49 @@ function EditIcon() {
);
}
+function getUserAccessLevel(user) {
+ return user?.accessLevel ?? membershipState.NON_MEMBER;
+}
+
+function canUserSeeEvent(event, user) {
+ const userId = user?._id != null ? String(user._id) : '';
+ const userAccess = getUserAccessLevel(user);
+
+ const isGlobalAdmin = userAccess >= membershipState.ADMIN;
+ const isEventAdmin = Array.isArray(event.admins) && userId
+ ? event.admins.includes(userId)
+ : false;
+
+ const status = event.status || 'draft';
+ const visibility = event.visibility || 'public';
+ const minimumVisibleRole = event.minimum_visible_role || '';
+
+ // Draft events are only visible to global admins or users listed in event.admins
+ if (status === 'draft') {
+ return isGlobalAdmin || isEventAdmin;
+ }
+
+ // Public → everyone
+ if (visibility === 'public') {
+ return true;
+ }
+
+ // Private → compare access levels directly
+ if (visibility === 'private') {
+ const requiredLevel = membershipState[minimumVisibleRole?.toUpperCase()];
+ if (requiredLevel === undefined) return false;
+
+ return userAccess >= requiredLevel;
+ }
+
+ return false;
+}
+
function EventCard({ event, user }) {
- const isAdmin = event.admins && user?._id && event.admins.includes(String(user._id));
+ const isEventAdmin =
+ Array.isArray(event.admins) && user?._id
+ ? event.admins.includes(String(user._id))
+ : false;
return (
@@ -83,7 +124,7 @@ function EventCard({ event, user }) {
{event.name || 'Untitled Event'}
- {isAdmin && (
+ {isEventAdmin && (
+
+ {event.status === 'draft' && (
+
+ Draft
+
+ )}
+ {event.visibility === 'private' && (
+
+ Private
+
+ )}
+
+
{(event.date || event.time) && (
@@ -139,6 +193,7 @@ export default function EventsPage() {
const [hasError, setHasError] = useState(false);
const isSCEventsEnabled = config.SCEvents?.ENABLED;
const canCreateEvent = user?.accessLevel >= membershipState.OFFICER;
+ const visibleEvents = events.filter((event) => canUserSeeEvent(event, user));
useEffect(() => {
if (!isSCEventsEnabled) {
@@ -210,15 +265,15 @@ export default function EventsPage() {
)}
- {!isLoading && !hasError && events.length === 0 && (
+ {!isLoading && !hasError && visibleEvents.length === 0 && (
No events available right now.
)}
- {!isLoading && !hasError && events.length > 0 && (
+ {!isLoading && !hasError && visibleEvents.length > 0 && (
- {events.map((event) => (
+ {visibleEvents.map((event) => (
))}