refactor(events): expand eventType taxonomy with central config
Replaces the four-value enum (community/workshop/social/showcase) with seven values: talk, workshop, community-meetup, coworking, peer-session, skills-share, info-session. Default is now community-meetup. Adds app/config/eventTypes.js as the single source of truth for value→label mapping. Updates the model enum, seed scripts, and admin event list/filter + admin dashboard to read from it via EVENT_TYPES and eventTypeLabel().
This commit is contained in:
parent
31144617d7
commit
2ffaf0ef09
6 changed files with 54 additions and 24 deletions
21
app/config/eventTypes.js
Normal file
21
app/config/eventTypes.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Central configuration for Ghost Guild event types.
|
||||
// Keep values in sync with the `eventType` enum in server/models/event.js.
|
||||
export const EVENT_TYPES = [
|
||||
{ value: "talk", label: "Talk / Presentation" },
|
||||
{ value: "workshop", label: "Workshop" },
|
||||
{ value: "community-meetup", label: "Community Meetup" },
|
||||
{ value: "coworking", label: "Co-working Session" },
|
||||
{ value: "peer-session", label: "Peer Session" },
|
||||
{ value: "skills-share", label: "Skills Share" },
|
||||
{ value: "info-session", label: "Info Session" },
|
||||
];
|
||||
|
||||
export const EVENT_TYPE_VALUES = EVENT_TYPES.map((t) => t.value);
|
||||
|
||||
const labelLookup = Object.fromEntries(
|
||||
EVENT_TYPES.map((t) => [t.value, t.label]),
|
||||
);
|
||||
|
||||
export function eventTypeLabel(value) {
|
||||
return labelLookup[value] || value || "";
|
||||
}
|
||||
|
|
@ -16,15 +16,12 @@
|
|||
<!-- Filters -->
|
||||
<div class="filter-bar">
|
||||
<div class="field" style="margin-bottom: 0; flex: 1;">
|
||||
<input v-model="searchQuery" placeholder="Search events..." />
|
||||
<input v-model="searchQuery" placeholder="Search events..." >
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 0;">
|
||||
<select v-model="typeFilter">
|
||||
<option value="all">All Types</option>
|
||||
<option value="community">Community</option>
|
||||
<option value="workshop">Workshop</option>
|
||||
<option value="social">Social</option>
|
||||
<option value="showcase">Showcase</option>
|
||||
<option v-for="t in EVENT_TYPES" :key="t.value" :value="t.value">{{ t.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 0;">
|
||||
|
|
@ -71,7 +68,7 @@
|
|||
<td class="col-title">
|
||||
<div class="event-title-cell">
|
||||
<div v-if="event.featureImage?.url && !event.featureImage?.publicId" class="event-thumb">
|
||||
<img :src="event.featureImage.url" :alt="event.title" @error="handleImageError($event)" />
|
||||
<img :src="event.featureImage.url" :alt="event.title" @error="handleImageError($event)" >
|
||||
</div>
|
||||
<div>
|
||||
<span class="event-name">{{ event.title }}</span>
|
||||
|
|
@ -89,7 +86,7 @@
|
|||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge" :class="event.eventType">{{ event.eventType }}</span>
|
||||
<span class="badge" :class="event.eventType">{{ eventTypeLabel(event.eventType) }}</span>
|
||||
</td>
|
||||
<td class="col-date">
|
||||
<span class="date-main">{{ formatDate(event) }}</span>
|
||||
|
|
@ -128,9 +125,9 @@
|
|||
</td>
|
||||
<td class="col-actions">
|
||||
<NuxtLink :to="`/events/${event.slug || String(event._id)}`" class="link-btn" title="View">View</NuxtLink>
|
||||
<button @click="editEvent(event)" class="link-btn" title="Edit">Edit</button>
|
||||
<button @click="duplicateEvent(event)" class="link-btn" title="Duplicate">Dup</button>
|
||||
<button @click="deleteEvent(event)" class="link-btn link-btn-danger" title="Delete">Del</button>
|
||||
<button class="link-btn" title="Edit" @click="editEvent(event)">Edit</button>
|
||||
<button class="link-btn" title="Duplicate" @click="duplicateEvent(event)">Dup</button>
|
||||
<button class="link-btn link-btn-danger" title="Delete" @click="deleteEvent(event)">Del</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
@ -169,7 +166,7 @@
|
|||
<td class="col-title">
|
||||
<div class="event-title-cell">
|
||||
<div v-if="event.featureImage?.url && !event.featureImage?.publicId" class="event-thumb">
|
||||
<img :src="event.featureImage.url" :alt="event.title" @error="handleImageError($event)" />
|
||||
<img :src="event.featureImage.url" :alt="event.title" @error="handleImageError($event)" >
|
||||
</div>
|
||||
<div>
|
||||
<span class="event-name">{{ event.title }}</span>
|
||||
|
|
@ -187,7 +184,7 @@
|
|||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge" :class="event.eventType">{{ event.eventType }}</span>
|
||||
<span class="badge" :class="event.eventType">{{ eventTypeLabel(event.eventType) }}</span>
|
||||
</td>
|
||||
<td class="col-date">
|
||||
<span class="date-main">{{ formatDate(event) }}</span>
|
||||
|
|
@ -226,9 +223,9 @@
|
|||
</td>
|
||||
<td class="col-actions">
|
||||
<NuxtLink :to="`/events/${event.slug || String(event._id)}`" class="link-btn" title="View">View</NuxtLink>
|
||||
<button @click="editEvent(event)" class="link-btn" title="Edit">Edit</button>
|
||||
<button @click="duplicateEvent(event)" class="link-btn" title="Duplicate">Dup</button>
|
||||
<button @click="deleteEvent(event)" class="link-btn link-btn-danger" title="Delete">Del</button>
|
||||
<button class="link-btn" title="Edit" @click="editEvent(event)">Edit</button>
|
||||
<button class="link-btn" title="Duplicate" @click="duplicateEvent(event)">Dup</button>
|
||||
<button class="link-btn link-btn-danger" title="Delete" @click="deleteEvent(event)">Del</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
@ -267,6 +264,8 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { EVENT_TYPES, eventTypeLabel } from '~/config/eventTypes'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'admin',
|
||||
middleware: 'admin',
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@
|
|||
<span class="item-sub">{{ formatDateTime(event.startDate) }}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<span class="badge" :class="event.eventType">{{ event.eventType }}</span>
|
||||
<span class="badge" :class="event.eventType">{{ eventTypeLabel(event.eventType) }}</span>
|
||||
<span class="item-date">{{ event.location || 'Online' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -106,6 +106,8 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { eventTypeLabel } from '~/config/eventTypes'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'admin',
|
||||
middleware: 'admin',
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const sampleEvents = [
|
|||
content: 'This informal meetup is perfect for connecting with other developers interested in cooperative business models. We\'ll have brief presentations, open discussions, and time for networking.\n\nAgenda:\n- Welcome & introductions\n- Member spotlight presentations\n- Open discussion on cooperative challenges and successes\n- Networking and social time',
|
||||
startDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7, 19, 0),
|
||||
endDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7, 21, 0),
|
||||
eventType: 'community',
|
||||
eventType: 'community-meetup',
|
||||
location: '#general',
|
||||
isOnline: true,
|
||||
membersOnly: false,
|
||||
|
|
@ -107,7 +107,7 @@ const sampleEvents = [
|
|||
content: 'Our quarterly showcase featuring presentations from Ghost Guild member studios. Learn about ongoing projects, cooperative development processes, and the unique challenges and benefits of collaborative game creation.\n\nFeatured presentations:\n- "Collaborative Level Design in Practice"\n- "Democratic Decision Making in Creative Projects"\n- "Balancing Individual Creativity with Group Consensus"\n- Q&A with presenting studios',
|
||||
startDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 21, 18, 30),
|
||||
endDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 21, 21, 0),
|
||||
eventType: 'showcase',
|
||||
eventType: 'skills-share',
|
||||
location: '#showcase',
|
||||
isOnline: true,
|
||||
membersOnly: true,
|
||||
|
|
@ -134,7 +134,7 @@ const sampleEvents = [
|
|||
content: 'Join us for a casual evening of celebration, networking, and community building. Perfect for new members to meet the community and for existing members to catch up.\n\nActivities:\n- Welcome reception\n- Casual networking\n- Community achievements celebration\n- Light refreshments provided\n- Optional lightning talks (5 min, informal)',
|
||||
startDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3, 18, 0),
|
||||
endDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3, 21, 0),
|
||||
eventType: 'social',
|
||||
eventType: 'community-meetup',
|
||||
location: '#social',
|
||||
isOnline: true,
|
||||
membersOnly: true,
|
||||
|
|
@ -234,7 +234,7 @@ const sampleEvents = [
|
|||
content: 'Our February meetup has been cancelled but will be rescheduled soon. Stay tuned for updates!',
|
||||
startDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 42, 19, 0),
|
||||
endDate: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 42, 21, 0),
|
||||
eventType: 'community',
|
||||
eventType: 'community-meetup',
|
||||
location: '#general',
|
||||
isOnline: true,
|
||||
membersOnly: false,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ async function seedSeriesEvents() {
|
|||
"Our monthly community meetup provides a relaxed environment to share your projects, get feedback, and connect with other developers interested in cooperative models.",
|
||||
startDate: new Date("2024-10-12T18:00:00.000Z"),
|
||||
endDate: new Date("2024-10-12T20:00:00.000Z"),
|
||||
eventType: "community",
|
||||
eventType: "community-meetup",
|
||||
location: "#community-meetup",
|
||||
isOnline: true,
|
||||
membersOnly: false,
|
||||
|
|
@ -176,7 +176,7 @@ async function seedSeriesEvents() {
|
|||
"Our monthly community meetup provides a relaxed environment to share your projects, get feedback, and connect with other developers interested in cooperative models.",
|
||||
startDate: new Date("2024-11-09T18:00:00.000Z"),
|
||||
endDate: new Date("2024-11-09T20:00:00.000Z"),
|
||||
eventType: "community",
|
||||
eventType: "community-meetup",
|
||||
location: "#community-meetup",
|
||||
isOnline: true,
|
||||
membersOnly: false,
|
||||
|
|
|
|||
|
|
@ -15,8 +15,16 @@ const eventSchema = new mongoose.Schema({
|
|||
endDate: { type: Date, required: true },
|
||||
eventType: {
|
||||
type: String,
|
||||
enum: ["community", "workshop", "social", "showcase"],
|
||||
default: "community",
|
||||
enum: [
|
||||
"talk",
|
||||
"workshop",
|
||||
"community-meetup",
|
||||
"coworking",
|
||||
"peer-session",
|
||||
"skills-share",
|
||||
"info-session",
|
||||
],
|
||||
default: "community-meetup",
|
||||
},
|
||||
// IANA timezone for interpreting datetime input and rendering the event time.
|
||||
displayTimezone: { type: String, default: "America/Toronto" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue