upcoming events dynamic

This commit is contained in:
srikanth_M 2026-08-06 10:10:02 +05:30
parent b42f8a9e69
commit ead2eef1b5
3 changed files with 397 additions and 28 deletions

View File

@ -1,13 +0,0 @@
# Database Configuration
DB_HOST=82.25.95.117
DB_USER=user_tamilculturewaterloo
DB_PASSWORD=Metatron@2025
DB_NAME=user_tamilculturewaterloo
# Server Configuration
PORT=3006
DB_CONNECTION_LIMIT=20
DB_ACQUIRE_TIMEOUT=60000
DB_TIMEOUT=60000
DB_IDLE_TIMEOUT=300000

366
server.js
View File

@ -157,6 +157,120 @@ async function ensureEventImagesSortOrderColumn() {
throw error; throw error;
} }
} }
async function ensureUpcomingEventsTable() {
try {
await pool.execute(`
CREATE TABLE IF NOT EXISTS upcoming_events (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
eventtitle VARCHAR(255),
slug VARCHAR(255),
date VARCHAR(255),
eventdate VARCHAR(255),
time VARCHAR(255),
location VARCHAR(255),
image TEXT,
eventimageurl TEXT,
link TEXT,
btn_text VARCHAR(255) DEFAULT 'Details Coming Soon',
btnText VARCHAR(255) DEFAULT 'Details Coming Soon',
description TEXT,
eventdescription TEXT,
admission VARCHAR(255),
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
const [columns] = await pool.execute("SHOW COLUMNS FROM upcoming_events");
const colNames = columns.map(col => col.Field);
if (colNames.includes('year')) {
try {
await pool.execute("ALTER TABLE upcoming_events MODIFY COLUMN year INT NULL DEFAULT 2026");
} catch (err) {
console.warn('Could not modify year column:', err.message);
}
}
const columnsToAdd = [
{ name: 'title', type: 'VARCHAR(255)' },
{ name: 'date', type: 'VARCHAR(255)' },
{ name: 'image', type: 'TEXT' },
{ name: 'description', type: 'TEXT' },
{ name: 'btn_text', type: "VARCHAR(255) DEFAULT 'Details Coming Soon'" },
{ name: 'admission', type: 'VARCHAR(255)' },
{ name: 'sort_order', type: 'INT DEFAULT 0' }
];
for (const col of columnsToAdd) {
if (!colNames.includes(col.name)) {
await pool.execute(`ALTER TABLE upcoming_events ADD COLUMN ${col.name} ${col.type}`);
}
}
console.log('✅ Verified upcoming_events table');
const [existing] = await pool.execute(
'SELECT id FROM upcoming_events WHERE title = ? OR eventtitle = ?',
['KW Multicultural Festival', 'KW Multicultural Festival']
);
if (existing.length === 0) {
const hasYearCol = colNames.includes('year');
if (hasYearCol) {
await pool.execute(
`INSERT INTO upcoming_events (year, title, eventtitle, slug, date, eventdate, time, location, image, eventimageurl, link, btn_text, btnText, description, eventdescription)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
2026,
'KW Multicultural Festival',
'KW Multicultural Festival',
'kw-multicultural-festival-2026',
'Jun 20, 2026 and Jun 21, 2026',
'Jun 20, 2026 and Jun 21, 2026',
'Details will be announced',
'Victoria Park, Kitchener',
'/assets/img/event/upcoming-event/multicultural-festival.webp',
'/assets/img/event/upcoming-event/multicultural-festival.webp',
'/upcoming-event/kw-multicultural-festival-2026',
'Details Coming Soon',
'Details Coming Soon',
'KW Multicultural Festival will take place on Jun 20-21, 2026 at Victoria Park, Kitchener. More details will be updated soon.',
'KW Multicultural Festival will take place on Jun 20-21, 2026 at Victoria Park, Kitchener. More details will be updated soon.'
]
);
} else {
await pool.execute(
`INSERT INTO upcoming_events (title, eventtitle, slug, date, eventdate, time, location, image, eventimageurl, link, btn_text, btnText, description, eventdescription)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
'KW Multicultural Festival',
'KW Multicultural Festival',
'kw-multicultural-festival-2026',
'Jun 20, 2026 and Jun 21, 2026',
'Jun 20, 2026 and Jun 21, 2026',
'Details will be announced',
'Victoria Park, Kitchener',
'/assets/img/event/upcoming-event/multicultural-festival.webp',
'/assets/img/event/upcoming-event/multicultural-festival.webp',
'/upcoming-event/kw-multicultural-festival-2026',
'Details Coming Soon',
'Details Coming Soon',
'KW Multicultural Festival will take place on Jun 20-21, 2026 at Victoria Park, Kitchener. More details will be updated soon.',
'KW Multicultural Festival will take place on Jun 20-21, 2026 at Victoria Park, Kitchener. More details will be updated soon.'
]
);
}
console.log('✅ Seeded initial upcoming event: KW Multicultural Festival');
}
} catch (error) {
console.error('❌ Failed to ensure upcoming_events table:', error.message);
throw error;
}
}
// Test database connection on startup // Test database connection on startup
async function testDatabaseConnection() { async function testDatabaseConnection() {
try { try {
@ -166,6 +280,7 @@ async function testDatabaseConnection() {
connection.release(); connection.release();
await ensureAdminUsersTable(); await ensureAdminUsersTable();
await ensureEventImagesSortOrderColumn(); await ensureEventImagesSortOrderColumn();
await ensureUpcomingEventsTable();
} catch (error) { } catch (error) {
console.error('❌ Database connection failed:', error.message); console.error('❌ Database connection failed:', error.message);
process.exit(1); process.exit(1);
@ -548,6 +663,253 @@ app.delete('/api/events/:id', async (req, res) => {
} }
}); });
// ===============================
// UPCOMING EVENTS CRUD ROUTES
// ===============================
// Helper function to slugify string
function slugify(text) {
if (!text) return '';
return text
.toString()
.toLowerCase()
.trim()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-'); // Replace multiple - with single -
}
// GET all upcoming events
app.get('/api/upcoming-events', async (req, res) => {
try {
const [rows] = await pool.execute('SELECT * FROM upcoming_events ORDER BY sort_order ASC, id DESC');
res.json({
success: true,
data: rows
});
} catch (error) {
console.error('Error fetching upcoming events:', error);
res.status(500).json({
success: false,
message: 'Error fetching upcoming events',
error: error.message
});
}
});
// GET single upcoming event by ID
app.get('/api/upcoming-events/:id', async (req, res) => {
try {
const [rows] = await pool.execute('SELECT * FROM upcoming_events WHERE id = ?', [req.params.id]);
if (rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Upcoming event not found'
});
}
res.json({
success: true,
data: rows[0]
});
} catch (error) {
console.error('Error fetching upcoming event:', error);
res.status(500).json({
success: false,
message: 'Error fetching upcoming event',
error: error.message
});
}
});
// GET single upcoming event by slug or ID
app.get('/api/upcoming-events/slug/:slug', async (req, res) => {
try {
const rawSlug = req.params.slug ? req.params.slug.trim() : '';
const cleanedSlug = slugify(rawSlug);
// 1. First priority: exact slug or ID match
let [rows] = await pool.execute(
'SELECT * FROM upcoming_events WHERE slug = ? OR slug = ? OR id = ?',
[rawSlug, cleanedSlug, rawSlug]
);
// 2. Second priority: title slug match fallback
if (rows.length === 0 && cleanedSlug) {
[rows] = await pool.execute(
'SELECT * FROM upcoming_events WHERE LOWER(REPLACE(title, " ", "-")) = ? OR LOWER(REPLACE(eventtitle, " ", "-")) = ?',
[cleanedSlug, cleanedSlug]
);
}
if (rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Upcoming event not found'
});
}
res.json({
success: true,
data: rows[0]
});
} catch (error) {
console.error('Error fetching upcoming event by slug:', error);
res.status(500).json({
success: false,
message: 'Error fetching upcoming event',
error: error.message
});
}
});
// POST create new upcoming event
app.post('/api/upcoming-events', async (req, res) => {
try {
const { title, eventtitle, slug, date, eventdate, time, location, venue, image, image_url, eventimageurl, link, btn_text, btnText, description, desc, eventdescription, admission } = req.body;
const inputTitle = title || eventtitle;
if (!inputTitle || String(inputTitle).trim() === '') {
return res.status(400).json({
success: false,
message: 'Event title is required'
});
}
const finalTitle = String(inputTitle).trim();
const generatedSlug = slugify(finalTitle);
const finalSlug = (slug && String(slug).trim()) ? slugify(String(slug).trim()) : generatedSlug;
const finalLocation = location || venue || '';
const finalImage = image || image_url || eventimageurl || '';
const finalBtnText = btn_text || btnText || 'Details Coming Soon';
const finalDesc = description || desc || eventdescription || '';
const finalLink = (link && String(link).trim() && !String(link).startsWith('/upcoming-event/'))
? String(link).trim()
: `/upcoming-event/${finalSlug}`;
const finalDate = date || eventdate || '';
const finalTime = time || '';
const finalAdmission = admission || null;
const [result] = await pool.execute(
`INSERT INTO upcoming_events (title, eventtitle, slug, date, eventdate, time, location, image, eventimageurl, link, btn_text, btnText, description, eventdescription, admission)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[finalTitle, finalTitle, finalSlug, finalDate, finalDate, finalTime, finalLocation, finalImage, finalImage, finalLink, finalBtnText, finalBtnText, finalDesc, finalDesc, finalAdmission]
);
res.status(201).json({
success: true,
message: 'Upcoming event created successfully',
data: {
id: result.insertId,
title: finalTitle,
slug: finalSlug,
link: finalLink
}
});
} catch (error) {
console.error('Error creating upcoming event:', error);
res.status(500).json({
success: false,
message: 'Error creating upcoming event',
error: error.message
});
}
});
// PUT update upcoming event (supports full or partial updates)
app.put('/api/upcoming-events/:id', async (req, res) => {
try {
const eventId = req.params.id;
// Fetch existing record
const [rows] = await pool.execute('SELECT * FROM upcoming_events WHERE id = ?', [eventId]);
if (rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Upcoming event not found'
});
}
const existing = rows[0];
const { title, eventtitle, slug, date, eventdate, time, location, venue, image, image_url, eventimageurl, link, btn_text, btnText, description, desc, eventdescription, admission, sort_order } = req.body || {};
const inputTitle = title || eventtitle || existing.title || existing.eventtitle;
if (!inputTitle || String(inputTitle).trim() === '') {
return res.status(400).json({
success: false,
message: 'Event title is required'
});
}
const finalTitle = String(inputTitle).trim();
const generatedSlug = slugify(finalTitle);
let finalSlug = generatedSlug;
if (slug && String(slug).trim()) {
const userSlug = slugify(String(slug).trim());
const oldTitleSlug = slugify(existing.title || existing.eventtitle || '');
if (userSlug !== oldTitleSlug) {
finalSlug = userSlug;
}
}
const finalLocation = location !== undefined ? location : (venue !== undefined ? venue : (existing.location || ''));
const finalImage = image !== undefined ? image : (image_url !== undefined ? image_url : (eventimageurl !== undefined ? eventimageurl : (existing.image || existing.eventimageurl || '')));
const finalBtnText = btn_text !== undefined ? btn_text : (btnText !== undefined ? btnText : (existing.btn_text || existing.btnText || 'Details Coming Soon'));
const finalDesc = description !== undefined ? description : (desc !== undefined ? desc : (eventdescription !== undefined ? eventdescription : (existing.description || existing.eventdescription || '')));
const finalLink = (link && String(link).trim() && !String(link).startsWith('/upcoming-event/'))
? String(link).trim()
: `/upcoming-event/${finalSlug}`;
const finalDate = date !== undefined ? date : (eventdate !== undefined ? eventdate : (existing.date || existing.eventdate || ''));
const finalTime = time !== undefined ? time : (existing.time || '');
const finalAdmission = admission !== undefined ? admission : existing.admission;
const finalSortOrder = sort_order !== undefined ? Number(sort_order) : (existing.sort_order || 0);
await pool.execute(
`UPDATE upcoming_events
SET title = ?, eventtitle = ?, slug = ?, date = ?, eventdate = ?, time = ?, location = ?, image = ?, eventimageurl = ?, link = ?, btn_text = ?, btnText = ?, description = ?, eventdescription = ?, admission = ?, sort_order = ?
WHERE id = ?`,
[finalTitle, finalTitle, finalSlug, finalDate, finalDate, finalTime, finalLocation, finalImage, finalImage, finalLink, finalBtnText, finalBtnText, finalDesc, finalDesc, finalAdmission, finalSortOrder, eventId]
);
res.json({
success: true,
message: 'Upcoming event updated successfully'
});
} catch (error) {
console.error('Error updating upcoming event:', error);
res.status(500).json({
success: false,
message: 'Error updating upcoming event',
error: error.message
});
}
});
// DELETE upcoming event
app.delete('/api/upcoming-events/:id', async (req, res) => {
try {
const [result] = await pool.execute('DELETE FROM upcoming_events WHERE id = ?', [req.params.id]);
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'Upcoming event not found'
});
}
res.json({
success: true,
message: 'Upcoming event deleted successfully'
});
} catch (error) {
console.error('Error deleting upcoming event:', error);
res.status(500).json({
success: false,
message: 'Error deleting upcoming event',
error: error.message
});
}
});
// =============================== // ===============================
// EVENT_IMAGES CRUD ROUTES // EVENT_IMAGES CRUD ROUTES
// =============================== // ===============================
@ -581,7 +943,7 @@ app.get('/api/event-images/event/:eventId', async (req, res) => {
const [rows] = await pool.execute( const [rows] = await pool.execute(
'SELECT * FROM event_images WHERE eventid = ? ORDER BY sort_order ASC, id ASC', 'SELECT * FROM event_images WHERE eventid = ? ORDER BY sort_order ASC, id ASC',
[req.params.eventId] [req.params.eventId]
); );
res.json({ res.json({
success: true, success: true,
data: rows data: rows
@ -711,7 +1073,7 @@ app.put('/api/event-images/reorder', async (req, res) => {
message: error.message, message: error.message,
code: error.code code: error.code
}); });
} }
}); });
app.post('/api/event-images/bulk', async (req, res) => { app.post('/api/event-images/bulk', async (req, res) => {

View File

@ -52,6 +52,26 @@ async function setup() {
`); `);
console.log('Verified event_images table exists.'); console.log('Verified event_images table exists.');
// Create upcoming_events table
await pool.execute(`
CREATE TABLE IF NOT EXISTS upcoming_events (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
slug VARCHAR(255),
date VARCHAR(255),
time VARCHAR(255),
location VARCHAR(255),
image TEXT,
link TEXT,
btn_text VARCHAR(255) DEFAULT 'Details Coming Soon',
description TEXT,
admission VARCHAR(255),
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
console.log('Verified upcoming_events table exists.');
// Check if admin user exists // Check if admin user exists
const [rows] = await pool.execute('SELECT * FROM admin_users WHERE email = ?', ['admin@example.com']); const [rows] = await pool.execute('SELECT * FROM admin_users WHERE email = ?', ['admin@example.com']);