Add landing page
This commit is contained in:
parent
3fea484585
commit
bce86ee840
47 changed files with 7119 additions and 439 deletions
364
scripts/add-coop-values-series.js
Normal file
364
scripts/add-coop-values-series.js
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function addCoopValuesSeries() {
|
||||
try {
|
||||
// Connect to MongoDB
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('Connected to MongoDB');
|
||||
|
||||
// Create the series
|
||||
const seriesData = {
|
||||
id: 'coop-values-into-practice-2025',
|
||||
title: 'Cooperative Values into Practice',
|
||||
description: 'A practical, region-agnostic foundation in cooperative values and governance for game studio founders interested in worker-centric, anti-capitalist models. Structured as a peer-driven workshop emphasizing reciprocal learning and sharing.',
|
||||
type: 'workshop_series',
|
||||
isVisible: true,
|
||||
isActive: true,
|
||||
targetCircles: ['founder', 'practitioner'],
|
||||
totalEvents: 6,
|
||||
createdBy: 'admin@ghostguild.org',
|
||||
tickets: {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
allowIndividualEventTickets: true,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Series Pass',
|
||||
description: 'Free access to all sessions in the Cooperative Values into Practice series for Ghost Guild members.'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Series Pass',
|
||||
description: 'Access to all 6 sessions in the Cooperative Values into Practice series',
|
||||
price: 150,
|
||||
quantity: 20
|
||||
},
|
||||
capacity: {
|
||||
total: 30
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 15
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check if series already exists
|
||||
let series = await Series.findOne({ id: seriesData.id });
|
||||
|
||||
if (series) {
|
||||
console.log('Series already exists, updating...');
|
||||
series = await Series.findOneAndUpdate(
|
||||
{ id: seriesData.id },
|
||||
seriesData,
|
||||
{ new: true, runValidators: true }
|
||||
);
|
||||
} else {
|
||||
console.log('Creating new series...');
|
||||
series = new Series(seriesData);
|
||||
await series.save();
|
||||
}
|
||||
|
||||
console.log('Series created/updated:', series.title);
|
||||
|
||||
// Base date for scheduling (adjust as needed)
|
||||
const baseDate = new Date('2025-11-01T18:00:00.000Z'); // Starting November 1, 2025, 6 PM UTC
|
||||
|
||||
// Create the events
|
||||
const eventsData = [
|
||||
{
|
||||
title: 'Module 0: Orientation',
|
||||
tagline: 'Welcome to Cooperative Values into Practice',
|
||||
description: 'Introduce the goals and format of the program, quick power & identity reflections, Baby Ghosts values, and create group agreements.',
|
||||
content: `## Welcome!
|
||||
|
||||
This orientation session kicks off our 6-month journey exploring cooperative values and governance for game studios.
|
||||
|
||||
## What We'll Cover
|
||||
|
||||
- Program goals and format
|
||||
- Power & identity reflections
|
||||
- Baby Ghosts values introduction
|
||||
- Creating our group agreements together
|
||||
|
||||
## Homework
|
||||
|
||||
Power Flower exercise to complete before Module 1
|
||||
|
||||
## Who Should Attend
|
||||
|
||||
This series is designed for:
|
||||
- Game studio founders interested in worker-centric models
|
||||
- Practitioners exploring cooperative structures
|
||||
- Anyone committed to anti-capitalist, democratic workplaces`,
|
||||
position: 0,
|
||||
weeks: 0
|
||||
},
|
||||
{
|
||||
title: 'Module 1: Principles',
|
||||
tagline: 'Understanding cooperative foundations',
|
||||
description: 'Explore the difference between coops and traditional studios, foundational cooperative principles, historical and cultural context, and how they challenge industry norms.',
|
||||
content: `## Cooperative Principles
|
||||
|
||||
Understanding what makes cooperatives different from traditional game studios.
|
||||
|
||||
## Topics
|
||||
|
||||
- Coops vs. traditional studios - key differences
|
||||
- Foundational cooperative principles
|
||||
- How cooperative values challenge industry norms
|
||||
- Historical and cultural context
|
||||
- Introduction to Coops for Creatives
|
||||
|
||||
## Activities
|
||||
|
||||
- Values Reflection exercise
|
||||
- Value Mapping workshop
|
||||
|
||||
## Between Sessions
|
||||
|
||||
Reflect on how cooperative principles align with your studio vision.`,
|
||||
position: 1,
|
||||
weeks: 3
|
||||
},
|
||||
{
|
||||
title: 'Module 2: Purpose',
|
||||
tagline: 'Alignment, agreement, and shared vision',
|
||||
description: 'Learn the difference between alignment, agreement, and false consensus. Reflect on cooperative origin stories, discuss financial needs and capacity, and map collective visions.',
|
||||
content: `## Finding Your Cooperative Purpose
|
||||
|
||||
Moving beyond false consensus to genuine alignment.
|
||||
|
||||
## Topics
|
||||
|
||||
- The difference between alignment, agreement, and false consensus
|
||||
- Reflecting on cooperative origin stories and values
|
||||
- Discussing financial needs, availability, and capacity
|
||||
- Mapping collective visions for scale and pace
|
||||
- Clarifying roles, expectations, and contribution levels
|
||||
|
||||
## Activities
|
||||
|
||||
- Origin Stories sharing
|
||||
- Solidarity Press exercise
|
||||
- "The Talk" - discussing money and capacity
|
||||
- Scale & Pace Alignment worksheets
|
||||
|
||||
## Between Sessions
|
||||
|
||||
Continue conversations about purpose and alignment with your team.`,
|
||||
position: 2,
|
||||
weeks: 4
|
||||
},
|
||||
{
|
||||
title: 'Module 3: Practices Part 1 - Meetings & Decision-Making',
|
||||
tagline: 'Democratic processes in action',
|
||||
description: 'Move from boss-decides or majority-rules to cooperative approaches. Learn to redesign hierarchical meetings and facilitate inclusive, productive discussions.',
|
||||
content: `## Democratic Practices: Meetings
|
||||
|
||||
Redesigning how we make decisions together.
|
||||
|
||||
## Topics
|
||||
|
||||
- Moving from boss-decides or majority-rules to cooperative approaches
|
||||
- Redesigning hierarchical meetings
|
||||
- Tips for facilitating inclusive, productive meetings
|
||||
- Consensus-building techniques
|
||||
- Managing power dynamics in meetings
|
||||
|
||||
## Activities
|
||||
|
||||
- Meeting redesign workshop
|
||||
- Facilitation practice
|
||||
- Decision-making simulations
|
||||
|
||||
## Between Sessions
|
||||
|
||||
Try implementing new meeting practices with your team.`,
|
||||
position: 3,
|
||||
weeks: 4
|
||||
},
|
||||
{
|
||||
title: 'Module 4: Practices Part 2 - Finances & Governance',
|
||||
tagline: 'Transparent systems for cooperative work',
|
||||
description: 'Design transparent financial practices and systems, reframe disagreement as valuable information, and establish clear governance structures.',
|
||||
content: `## Democratic Practices: Money & Structure
|
||||
|
||||
Building transparent financial and governance systems.
|
||||
|
||||
## Topics
|
||||
|
||||
- Designing transparent financial practices and systems
|
||||
- Open book management for cooperatives
|
||||
- Reframing disagreement from failure to valuable information
|
||||
- Establishing clear governance systems
|
||||
- Creating accountability without hierarchy
|
||||
|
||||
## Activities
|
||||
|
||||
- Financial transparency workshop
|
||||
- Governance structure design
|
||||
- Conflict as data reframing
|
||||
|
||||
## Between Sessions
|
||||
|
||||
Draft governance documents or financial transparency practices for your studio.`,
|
||||
position: 4,
|
||||
weeks: 4
|
||||
},
|
||||
{
|
||||
title: 'Module 5: Pathways Forward',
|
||||
tagline: 'Your cooperative journey continues',
|
||||
description: 'Integrate learnings, share next steps, create accountability partnerships, and celebrate the work done together.',
|
||||
content: `## Moving Forward Together
|
||||
|
||||
Bringing it all together and planning your next steps.
|
||||
|
||||
## Topics
|
||||
|
||||
- Integrating everything we've learned
|
||||
- Individual and collective action plans
|
||||
- Creating accountability partnerships
|
||||
- Resources for ongoing learning
|
||||
- Building a community of practice
|
||||
|
||||
## Activities
|
||||
|
||||
- Action planning workshop
|
||||
- Accountability partner matching
|
||||
- Celebration and reflection
|
||||
|
||||
## What's Next
|
||||
|
||||
Continue your cooperative journey with Ghost Guild support and your cohort connections.`,
|
||||
position: 5,
|
||||
weeks: 4
|
||||
}
|
||||
];
|
||||
|
||||
let currentDate = new Date(baseDate);
|
||||
const createdEvents = [];
|
||||
|
||||
for (const eventData of eventsData) {
|
||||
// Calculate dates
|
||||
const startDate = new Date(currentDate);
|
||||
const endDate = new Date(startDate);
|
||||
endDate.setHours(endDate.getHours() + 2); // 2-hour sessions
|
||||
|
||||
const eventPayload = {
|
||||
title: eventData.title,
|
||||
tagline: eventData.tagline,
|
||||
description: eventData.description,
|
||||
content: eventData.content,
|
||||
startDate,
|
||||
endDate,
|
||||
eventType: 'workshop',
|
||||
location: '#ghost-guild-workshops', // Slack channel
|
||||
isOnline: true,
|
||||
isVisible: true,
|
||||
membersOnly: false,
|
||||
series: {
|
||||
id: series.id,
|
||||
title: series.title,
|
||||
description: series.description,
|
||||
type: series.type,
|
||||
position: eventData.position + 1, // 1-indexed for display
|
||||
totalEvents: 6,
|
||||
isSeriesEvent: true
|
||||
},
|
||||
tickets: {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
seriesTicketReference: series._id,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Ticket',
|
||||
description: 'Free for Ghost Guild members'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Single Session Ticket',
|
||||
description: 'Attend this individual session',
|
||||
price: 30,
|
||||
quantity: 10
|
||||
},
|
||||
capacity: {
|
||||
total: 30
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 10
|
||||
}
|
||||
},
|
||||
targetCircles: ['founder', 'practitioner'],
|
||||
registrationRequired: true,
|
||||
createdBy: 'admin@ghostguild.org'
|
||||
};
|
||||
|
||||
// Check if event already exists (by title and series)
|
||||
const existingEvent = await Event.findOne({
|
||||
title: eventPayload.title,
|
||||
'series.id': series.id
|
||||
});
|
||||
|
||||
let event;
|
||||
if (existingEvent) {
|
||||
console.log(`Updating existing event: ${eventPayload.title}`);
|
||||
event = await Event.findByIdAndUpdate(
|
||||
existingEvent._id,
|
||||
eventPayload,
|
||||
{ new: true, runValidators: true }
|
||||
);
|
||||
} else {
|
||||
console.log(`Creating event: ${eventPayload.title}`);
|
||||
event = new Event(eventPayload);
|
||||
await event.save();
|
||||
}
|
||||
|
||||
createdEvents.push(event);
|
||||
|
||||
// Move to next session date (add weeks)
|
||||
if (eventData.weeks > 0) {
|
||||
currentDate.setDate(currentDate.getDate() + (eventData.weeks * 7));
|
||||
}
|
||||
}
|
||||
|
||||
// Update series with date range from events
|
||||
const firstEvent = createdEvents[0];
|
||||
const lastEvent = createdEvents[createdEvents.length - 1];
|
||||
|
||||
series.startDate = firstEvent.startDate;
|
||||
series.endDate = lastEvent.endDate;
|
||||
series.totalEvents = createdEvents.length;
|
||||
await series.save();
|
||||
|
||||
console.log('\n✅ Successfully created/updated series and all events!');
|
||||
console.log(`\nSeries: ${series.title}`);
|
||||
console.log(`Events created: ${createdEvents.length}`);
|
||||
console.log(`Date range: ${series.startDate.toLocaleDateString()} - ${series.endDate.toLocaleDateString()}`);
|
||||
|
||||
console.log('\nEvents:');
|
||||
createdEvents.forEach((event, index) => {
|
||||
console.log(` ${index + 1}. ${event.title} - ${event.startDate.toLocaleDateString()}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
console.log('\nDatabase connection closed');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
addCoopValuesSeries();
|
||||
334
scripts/add-to-remote-db.js
Normal file
334
scripts/add-to-remote-db.js
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
import Event from '../server/models/event.js';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
const MONGODB_URI = process.env.NUXT_MONGODB_URI || process.env.MONGODB_URI;
|
||||
|
||||
async function addToRemoteDB() {
|
||||
try {
|
||||
console.log('Connecting to remote MongoDB...');
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('✓ Connected');
|
||||
|
||||
// Check existing series
|
||||
const existingSeries = await Series.findOne({ id: 'cooperative-values-into-practice' });
|
||||
|
||||
if (existingSeries) {
|
||||
console.log(`\n✓ Found existing series: ${existingSeries.title}`);
|
||||
console.log(` Series _id: ${existingSeries._id}`);
|
||||
} else {
|
||||
console.log('\nSeries not found, creating new one...');
|
||||
const newSeries = new Series({
|
||||
id: 'cooperative-values-into-practice',
|
||||
title: 'Cooperative Values into Practice',
|
||||
description: 'A practical, region-agnostic foundation in cooperative values and governance for game studio founders interested in worker-centric, anti-capitalist models. Structured as a peer-driven workshop emphasizing reciprocal learning and sharing.',
|
||||
type: 'workshop_series',
|
||||
isVisible: true,
|
||||
isActive: true,
|
||||
targetCircles: ['founder', 'practitioner'],
|
||||
totalEvents: 6,
|
||||
createdBy: 'admin@ghostguild.org',
|
||||
tickets: {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
allowIndividualEventTickets: true,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Series Pass',
|
||||
description: 'Free access to all sessions for Ghost Guild members.'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Series Pass',
|
||||
description: 'Access to all 6 sessions',
|
||||
price: 150,
|
||||
quantity: 20,
|
||||
sold: 0,
|
||||
reserved: 0
|
||||
},
|
||||
capacity: {
|
||||
total: 30,
|
||||
reserved: 0
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 15,
|
||||
entries: []
|
||||
}
|
||||
}
|
||||
});
|
||||
await newSeries.save();
|
||||
console.log('✓ Series created');
|
||||
}
|
||||
|
||||
const series = await Series.findOne({ id: 'cooperative-values-into-practice' });
|
||||
|
||||
// Check existing events
|
||||
const existingEvents = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'series.isSeriesEvent': true
|
||||
}).select('title').lean();
|
||||
|
||||
console.log(`\nCurrent events in series: ${existingEvents.length}`);
|
||||
existingEvents.forEach(e => console.log(` - ${e.title}`));
|
||||
|
||||
// Define the 6 modules
|
||||
const baseDate = new Date('2025-11-01T18:00:00.000Z');
|
||||
const modulesData = [
|
||||
{
|
||||
title: 'Module 0: Orientation',
|
||||
tagline: 'Welcome to Cooperative Values into Practice',
|
||||
description: 'Introduce the goals and format of the program, quick power & identity reflections, Baby Ghosts values, and create group agreements.',
|
||||
content: `## Welcome!
|
||||
|
||||
This orientation session kicks off our 6-month journey exploring cooperative values and governance for game studios.
|
||||
|
||||
## What We'll Cover
|
||||
|
||||
- Program goals and format
|
||||
- Power & identity reflections
|
||||
- Baby Ghosts values introduction
|
||||
- Creating our group agreements together
|
||||
|
||||
## Homework
|
||||
|
||||
Power Flower exercise to complete before Module 1`,
|
||||
position: 1,
|
||||
weeksFromPrevious: 0
|
||||
},
|
||||
{
|
||||
title: 'Module 1: Principles',
|
||||
tagline: 'Understanding cooperative foundations',
|
||||
description: 'Explore the difference between coops and traditional studios, foundational cooperative principles, historical and cultural context, and how they challenge industry norms.',
|
||||
content: `## Cooperative Principles
|
||||
|
||||
Understanding what makes cooperatives different from traditional game studios.
|
||||
|
||||
## Topics
|
||||
|
||||
- Coops vs. traditional studios
|
||||
- Foundational cooperative principles
|
||||
- How cooperative values challenge industry norms
|
||||
- Historical and cultural context
|
||||
- Coops for Creatives
|
||||
|
||||
## Activities
|
||||
|
||||
- Values Reflection exercise
|
||||
- Value Mapping workshop`,
|
||||
position: 2,
|
||||
weeksFromPrevious: 3
|
||||
},
|
||||
{
|
||||
title: 'Module 2: Purpose',
|
||||
tagline: 'Alignment, agreement, and shared vision',
|
||||
description: 'Learn the difference between alignment, agreement, and false consensus. Reflect on cooperative origin stories, discuss financial needs and capacity, and map collective visions.',
|
||||
content: `## Finding Your Cooperative Purpose
|
||||
|
||||
Moving beyond false consensus to genuine alignment.
|
||||
|
||||
## Topics
|
||||
|
||||
- Alignment vs. agreement vs. false consensus
|
||||
- Reflecting on cooperative origin stories
|
||||
- Discussing financial needs and capacity
|
||||
- Mapping collective visions for scale and pace
|
||||
- Clarifying roles and expectations
|
||||
|
||||
## Activities
|
||||
|
||||
- Origin Stories sharing
|
||||
- Solidarity Press exercise
|
||||
- "The Talk" - discussing money
|
||||
- Scale & Pace Alignment worksheets`,
|
||||
position: 3,
|
||||
weeksFromPrevious: 4
|
||||
},
|
||||
{
|
||||
title: 'Module 3: Practices Part 1 - Meetings & Decision-Making',
|
||||
tagline: 'Democratic processes in action',
|
||||
description: 'Move from boss-decides or majority-rules to cooperative approaches. Learn to redesign hierarchical meetings and facilitate inclusive, productive discussions.',
|
||||
content: `## Democratic Practices: Meetings
|
||||
|
||||
Redesigning how we make decisions together.
|
||||
|
||||
## Topics
|
||||
|
||||
- Moving from boss-decides to cooperative approaches
|
||||
- Redesigning hierarchical meetings
|
||||
- Facilitating inclusive, productive meetings
|
||||
- Consensus-building techniques
|
||||
- Managing power dynamics
|
||||
|
||||
## Activities
|
||||
|
||||
- Meeting redesign workshop
|
||||
- Facilitation practice
|
||||
- Decision-making simulations`,
|
||||
position: 4,
|
||||
weeksFromPrevious: 4
|
||||
},
|
||||
{
|
||||
title: 'Module 4: Practices Part 2 - Finances & Governance',
|
||||
tagline: 'Transparent systems for cooperative work',
|
||||
description: 'Design transparent financial practices and systems, reframe disagreement as valuable information, and establish clear governance structures.',
|
||||
content: `## Democratic Practices: Money & Structure
|
||||
|
||||
Building transparent financial and governance systems.
|
||||
|
||||
## Topics
|
||||
|
||||
- Designing transparent financial practices
|
||||
- Open book management
|
||||
- Reframing disagreement as valuable information
|
||||
- Establishing clear governance systems
|
||||
- Creating accountability without hierarchy
|
||||
|
||||
## Activities
|
||||
|
||||
- Financial transparency workshop
|
||||
- Governance structure design
|
||||
- Conflict as data reframing`,
|
||||
position: 5,
|
||||
weeksFromPrevious: 4
|
||||
},
|
||||
{
|
||||
title: 'Module 5: Pathways Forward',
|
||||
tagline: 'Your cooperative journey continues',
|
||||
description: 'Integrate learnings, share next steps, create accountability partnerships, and celebrate the work done together.',
|
||||
content: `## Moving Forward Together
|
||||
|
||||
Bringing it all together and planning your next steps.
|
||||
|
||||
## Topics
|
||||
|
||||
- Integrating everything we've learned
|
||||
- Individual and collective action plans
|
||||
- Creating accountability partnerships
|
||||
- Resources for ongoing learning
|
||||
- Building a community of practice
|
||||
|
||||
## Activities
|
||||
|
||||
- Action planning workshop
|
||||
- Accountability partner matching
|
||||
- Celebration and reflection`,
|
||||
position: 6,
|
||||
weeksFromPrevious: 4
|
||||
}
|
||||
];
|
||||
|
||||
let currentDate = new Date(baseDate);
|
||||
let created = 0;
|
||||
let updated = 0;
|
||||
|
||||
for (const moduleData of modulesData) {
|
||||
const startDate = new Date(currentDate);
|
||||
const endDate = new Date(startDate);
|
||||
endDate.setHours(endDate.getHours() + 2);
|
||||
|
||||
const eventPayload = {
|
||||
title: moduleData.title,
|
||||
tagline: moduleData.tagline,
|
||||
description: moduleData.description,
|
||||
content: moduleData.content,
|
||||
startDate,
|
||||
endDate,
|
||||
eventType: 'workshop',
|
||||
location: '#ghost-guild-workshops',
|
||||
isOnline: true,
|
||||
isVisible: true,
|
||||
membersOnly: false,
|
||||
series: {
|
||||
id: series.id,
|
||||
title: series.title,
|
||||
description: series.description,
|
||||
type: series.type,
|
||||
position: moduleData.position,
|
||||
totalEvents: 6,
|
||||
isSeriesEvent: true
|
||||
},
|
||||
tickets: {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
seriesTicketReference: series._id,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Ticket',
|
||||
description: 'Free for Ghost Guild members'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Single Session Ticket',
|
||||
description: 'Attend this individual session',
|
||||
price: 30,
|
||||
quantity: 10
|
||||
},
|
||||
capacity: {
|
||||
total: 30
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 10
|
||||
}
|
||||
},
|
||||
targetCircles: ['founder', 'practitioner'],
|
||||
registrationRequired: true,
|
||||
createdBy: 'admin@ghostguild.org'
|
||||
};
|
||||
|
||||
const existing = await Event.findOne({
|
||||
title: moduleData.title,
|
||||
'series.id': 'cooperative-values-into-practice'
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await Event.findByIdAndUpdate(existing._id, eventPayload);
|
||||
updated++;
|
||||
console.log(` ✓ Updated: ${moduleData.title}`);
|
||||
} else {
|
||||
const newEvent = new Event(eventPayload);
|
||||
await newEvent.save();
|
||||
created++;
|
||||
console.log(` ✓ Created: ${moduleData.title}`);
|
||||
}
|
||||
|
||||
// Move to next date
|
||||
if (moduleData.weeksFromPrevious > 0) {
|
||||
currentDate.setDate(currentDate.getDate() + (moduleData.weeksFromPrevious * 7));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n✅ Success!`);
|
||||
console.log(` Created: ${created} events`);
|
||||
console.log(` Updated: ${updated} events`);
|
||||
|
||||
// Verify final count
|
||||
const finalEvents = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'series.isSeriesEvent': true
|
||||
}).select('title series.position').sort({ 'series.position': 1 }).lean();
|
||||
|
||||
console.log(`\n📋 Total events in series: ${finalEvents.length}`);
|
||||
finalEvents.forEach(e => {
|
||||
console.log(` ${e.series.position}. ${e.title}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
addToRemoteDB();
|
||||
32
scripts/check-events.js
Normal file
32
scripts/check-events.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function checkEvents() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('Connected to MongoDB');
|
||||
|
||||
// Check events with the series
|
||||
const events = await Event.find({
|
||||
'series.id': 'coop-values-into-practice-2025'
|
||||
}).select('title series').lean();
|
||||
|
||||
console.log(`\nFound ${events.length} events for series 'coop-values-into-practice-2025'\n`);
|
||||
|
||||
events.forEach((event, index) => {
|
||||
console.log(`${index + 1}. ${event.title}`);
|
||||
console.log(` series.id: ${event.series?.id}`);
|
||||
console.log(` series.isSeriesEvent: ${event.series?.isSeriesEvent}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkEvents();
|
||||
31
scripts/check-old-event.js
Normal file
31
scripts/check-old-event.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function check() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
|
||||
const oldEvent = await Event.findOne({ title: 'Session 0: Orientation' }).lean();
|
||||
|
||||
if (oldEvent) {
|
||||
console.log('Found old "Session 0: Orientation" event:');
|
||||
console.log(` ID: ${oldEvent._id}`);
|
||||
console.log(` series.id: ${oldEvent.series?.id}`);
|
||||
console.log(` series.title: ${oldEvent.series?.title}`);
|
||||
console.log('\nDeleting old event...');
|
||||
await Event.deleteOne({ _id: oldEvent._id });
|
||||
console.log('✓ Deleted');
|
||||
} else {
|
||||
console.log('No old "Session 0: Orientation" event found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
27
scripts/check-series.js
Normal file
27
scripts/check-series.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function checkSeries() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('Connected to MongoDB');
|
||||
|
||||
const allSeries = await Series.find({}).lean();
|
||||
console.log(`\nTotal series: ${allSeries.length}\n`);
|
||||
allSeries.forEach(s => {
|
||||
console.log(`ID: ${s.id}`);
|
||||
console.log(`Title: ${s.title}`);
|
||||
console.log(`_id: ${s._id}`);
|
||||
console.log('---');
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkSeries();
|
||||
27
scripts/cleanup-old-event.js
Normal file
27
scripts/cleanup-old-event.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Event from '../server/models/event.js';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
const MONGODB_URI = process.env.NUXT_MONGODB_URI || process.env.MONGODB_URI;
|
||||
|
||||
async function cleanup() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
|
||||
// Delete the old Session 0 event (the one without a position)
|
||||
const result = await Event.deleteOne({
|
||||
title: 'Session 0: Orientation',
|
||||
'series.position': { $exists: false }
|
||||
});
|
||||
|
||||
console.log(`✓ Deleted ${result.deletedCount} old event(s)`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
cleanup();
|
||||
94
scripts/create-correct-series.js
Normal file
94
scripts/create-correct-series.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function createCorrectSeries() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('Connected to MongoDB');
|
||||
|
||||
// Delete the incorrectly named series
|
||||
await Series.deleteOne({ id: 'coop-values-into-practice-2025' });
|
||||
console.log('✓ Deleted old series');
|
||||
|
||||
// Create series with the correct ID that matches the old event
|
||||
const series = new Series({
|
||||
id: 'cooperative-values-into-practice',
|
||||
title: 'Cooperative Values into Practice',
|
||||
description: 'A practical, region-agnostic foundation in cooperative values and governance for game studio founders interested in worker-centric, anti-capitalist models. Structured as a peer-driven workshop emphasizing reciprocal learning and sharing.',
|
||||
type: 'workshop_series',
|
||||
isVisible: true,
|
||||
isActive: true,
|
||||
targetCircles: ['founder', 'practitioner'],
|
||||
totalEvents: 6,
|
||||
createdBy: 'admin@ghostguild.org',
|
||||
tickets: {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
allowIndividualEventTickets: true,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Series Pass',
|
||||
description: 'Free access to all sessions for Ghost Guild members.'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Series Pass',
|
||||
description: 'Access to all 6 sessions',
|
||||
price: 150,
|
||||
quantity: 20,
|
||||
sold: 0,
|
||||
reserved: 0
|
||||
},
|
||||
capacity: {
|
||||
total: 30,
|
||||
reserved: 0
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 15,
|
||||
entries: []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await series.save();
|
||||
console.log('✓ Created series with ID: cooperative-values-into-practice');
|
||||
|
||||
// Update all events to use this series ID
|
||||
const result = await Event.updateMany(
|
||||
{ 'series.id': 'coop-values-into-practice-2025' },
|
||||
{
|
||||
$set: {
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'tickets.seriesTicketReference': series._id
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`✓ Updated ${result.modifiedCount} events`);
|
||||
|
||||
// Verify
|
||||
const events = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'series.isSeriesEvent': true
|
||||
}).select('title series.position').sort({ 'series.position': 1 });
|
||||
|
||||
console.log(`\n✅ Success! Series has ${events.length} events:`);
|
||||
events.forEach(e => {
|
||||
console.log(` ${e.series.position}. ${e.title}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
createCorrectSeries();
|
||||
34
scripts/debug-series.js
Normal file
34
scripts/debug-series.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function debug() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
|
||||
const series = await Series.find({}).lean();
|
||||
console.log('Series in DB:');
|
||||
series.forEach(s => console.log(` ${s.id}: ${s.title}`));
|
||||
|
||||
console.log('\nAll events with series.id:');
|
||||
const allEvents = await Event.find({ 'series.id': { $exists: true } })
|
||||
.select('title series.id series.isSeriesEvent')
|
||||
.lean();
|
||||
|
||||
console.log(`Total: ${allEvents.length}`);
|
||||
allEvents.forEach(e => {
|
||||
console.log(` - ${e.title}`);
|
||||
console.log(` series.id: ${e.series?.id}`);
|
||||
console.log(` series.isSeriesEvent: ${e.series?.isSeriesEvent}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
debug();
|
||||
42
scripts/diagnose-query.js
Normal file
42
scripts/diagnose-query.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function diagnose() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
|
||||
console.log('Query 1: Find with series.id AND series.isSeriesEvent');
|
||||
const events1 = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'series.isSeriesEvent': true
|
||||
}).select('title series').lean();
|
||||
console.log(` Found: ${events1.length} events`);
|
||||
events1.forEach(e => console.log(` - ${e.title} (isSeriesEvent: ${e.series?.isSeriesEvent})`));
|
||||
|
||||
console.log('\nQuery 2: Find with just series.id');
|
||||
const events2 = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice'
|
||||
}).select('title series').lean();
|
||||
console.log(` Found: ${events2.length} events`);
|
||||
events2.forEach(e => console.log(` - ${e.title} (isSeriesEvent: ${e.series?.isSeriesEvent})`));
|
||||
|
||||
console.log('\nChecking one Module event directly:');
|
||||
const module0 = await Event.findOne({ title: 'Module 0: Orientation' }).select('series').lean();
|
||||
if (module0) {
|
||||
console.log(` series.id: "${module0.series?.id}"`);
|
||||
console.log(` series.isSeriesEvent: ${module0.series?.isSeriesEvent}`);
|
||||
console.log(` series.title: "${module0.series?.title}"`);
|
||||
} else {
|
||||
console.log(' NOT FOUND');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
diagnose();
|
||||
96
scripts/fix-series-id.js
Normal file
96
scripts/fix-series-id.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function fixSeriesId() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('Connected to MongoDB');
|
||||
|
||||
// Check existing series
|
||||
const allSeries = await Series.find({}).select('id title').lean();
|
||||
console.log('\nExisting series:');
|
||||
allSeries.forEach(s => console.log(` - ${s.id}: ${s.title}`));
|
||||
|
||||
// Delete the new series we created
|
||||
await Series.deleteOne({ id: 'coop-values-into-practice-2025' });
|
||||
console.log('\n✓ Deleted duplicate series: coop-values-into-practice-2025');
|
||||
|
||||
// Update the old series with new data
|
||||
const updatedSeries = await Series.findOneAndUpdate(
|
||||
{ id: 'cooperative-values-into-practice' },
|
||||
{
|
||||
title: 'Cooperative Values into Practice',
|
||||
description: 'A practical, region-agnostic foundation in cooperative values and governance for game studio founders interested in worker-centric, anti-capitalist models. Structured as a peer-driven workshop emphasizing reciprocal learning and sharing.',
|
||||
type: 'workshop_series',
|
||||
isVisible: true,
|
||||
isActive: true,
|
||||
targetCircles: ['founder', 'practitioner'],
|
||||
totalEvents: 6,
|
||||
tickets: {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
allowIndividualEventTickets: true,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Series Pass',
|
||||
description: 'Free access to all sessions in the Cooperative Values into Practice series for Ghost Guild members.'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Series Pass',
|
||||
description: 'Access to all 6 sessions in the Cooperative Values into Practice series',
|
||||
price: 150,
|
||||
quantity: 20
|
||||
},
|
||||
capacity: {
|
||||
total: 30
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 15
|
||||
}
|
||||
}
|
||||
},
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
console.log('✓ Updated existing series');
|
||||
|
||||
// Update all the new events to use the old series ID
|
||||
const result = await Event.updateMany(
|
||||
{ 'series.id': 'coop-values-into-practice-2025' },
|
||||
{
|
||||
$set: {
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'seriesTicketReference': updatedSeries._id
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`✓ Updated ${result.modifiedCount} events to use series ID: cooperative-values-into-practice`);
|
||||
|
||||
// Verify the events
|
||||
const events = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'series.isSeriesEvent': true
|
||||
}).select('title series.position startDate').sort({ 'series.position': 1 });
|
||||
|
||||
console.log(`\n✓ Found ${events.length} events in the series:`);
|
||||
events.forEach(e => {
|
||||
console.log(` ${e.series.position}. ${e.title} - ${e.startDate.toLocaleDateString()}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
fixSeriesId();
|
||||
37
scripts/list-all-series-events.js
Normal file
37
scripts/list-all-series-events.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function listAll() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
|
||||
// Group events by series ID
|
||||
const allEvents = await Event.find({ 'series.isSeriesEvent': true })
|
||||
.select('title series.id')
|
||||
.sort({ 'series.id': 1, 'series.position': 1 })
|
||||
.lean();
|
||||
|
||||
const grouped = {};
|
||||
allEvents.forEach(e => {
|
||||
const sid = e.series?.id || 'unknown';
|
||||
if (!grouped[sid]) grouped[sid] = [];
|
||||
grouped[sid].push(e.title);
|
||||
});
|
||||
|
||||
console.log('Events grouped by series.id:\n');
|
||||
Object.entries(grouped).forEach(([sid, events]) => {
|
||||
console.log(`${sid} (${events.length} events):`);
|
||||
events.forEach(e => console.log(` - ${e}`));
|
||||
console.log('');
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
listAll();
|
||||
99
scripts/merge-series.js
Normal file
99
scripts/merge-series.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import mongoose from 'mongoose';
|
||||
import Series from '../server/models/series.js';
|
||||
import Event from '../server/models/event.js';
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ghostguild';
|
||||
|
||||
async function mergeSeries() {
|
||||
try {
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('Connected to MongoDB');
|
||||
|
||||
// Get both series
|
||||
const oldSeries = await Series.findOne({ id: 'cooperative-values-into-practice' });
|
||||
const newSeries = await Series.findOne({ id: 'coop-values-into-practice-2025' });
|
||||
|
||||
console.log(`\nOld series _id: ${oldSeries?._id || 'NOT FOUND'}`);
|
||||
console.log(`New series _id: ${newSeries?._id || 'NOT FOUND'}`);
|
||||
|
||||
if (!oldSeries) {
|
||||
console.log('\nError: Old series not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the old series with new ticketing and metadata
|
||||
oldSeries.tickets = {
|
||||
enabled: true,
|
||||
requiresSeriesTicket: false,
|
||||
allowIndividualEventTickets: true,
|
||||
currency: 'CAD',
|
||||
member: {
|
||||
available: true,
|
||||
isFree: true,
|
||||
price: 0,
|
||||
name: 'Member Series Pass',
|
||||
description: 'Free access to all sessions in the Cooperative Values into Practice series for Ghost Guild members.'
|
||||
},
|
||||
public: {
|
||||
available: true,
|
||||
name: 'Series Pass',
|
||||
description: 'Access to all 6 sessions in the Cooperative Values into Practice series',
|
||||
price: 150,
|
||||
quantity: 20,
|
||||
sold: 0,
|
||||
reserved: 0
|
||||
},
|
||||
capacity: {
|
||||
total: 30,
|
||||
reserved: 0
|
||||
},
|
||||
waitlist: {
|
||||
enabled: true,
|
||||
maxSize: 15,
|
||||
entries: []
|
||||
}
|
||||
};
|
||||
oldSeries.targetCircles = ['founder', 'practitioner'];
|
||||
oldSeries.totalEvents = 6;
|
||||
|
||||
await oldSeries.save();
|
||||
console.log('\n✓ Updated old series with new configuration');
|
||||
|
||||
// Update all new events to use the old series ID
|
||||
const result = await Event.updateMany(
|
||||
{ 'series.id': 'coop-values-into-practice-2025' },
|
||||
{
|
||||
$set: {
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'tickets.seriesTicketReference': oldSeries._id
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`✓ Updated ${result.modifiedCount} events to use series ID: cooperative-values-into-practice`);
|
||||
|
||||
// Delete the new duplicate series
|
||||
if (newSeries) {
|
||||
await Series.deleteOne({ id: 'coop-values-into-practice-2025' });
|
||||
console.log('✓ Deleted duplicate series: coop-values-into-practice-2025');
|
||||
}
|
||||
|
||||
// Verify
|
||||
const events = await Event.find({
|
||||
'series.id': 'cooperative-values-into-practice',
|
||||
'series.isSeriesEvent': true
|
||||
}).select('title series.position startDate').sort({ 'series.position': 1 });
|
||||
|
||||
console.log(`\n✅ Series now has ${events.length} events:`);
|
||||
events.forEach(e => {
|
||||
console.log(` ${e.series.position}. ${e.title} - ${e.startDate.toLocaleDateString()}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
mergeSeries();
|
||||
Loading…
Add table
Add a link
Reference in a new issue