mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Introduce an information system for important announcements
Implement a new announcement system with priority levels, role-based access, and store-specific filtering, replacing the customer order statistics card on the dashboard. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e437ffed-c31b-44eb-9fd5-b6f9faa8e7ce Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/e437ffed-c31b-44eb-9fd5-b6f9faa8e7ce/biGr9ER
This commit is contained in:
1 parent
5688450117
commit
30cfc4b3de
9 files changed
+729
-47
No files matched your search
@@ -0,0 +1,27 @@
|
||||
-- Migration: Create announcements table
|
||||
-- Date: 2025-08-16
|
||||
-- Description: Create announcements table for admin-managed information system
|
||||
|
||||
CREATE TABLE IF NOT EXISTS announcements (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'normal' CHECK (priority IN ('normal', 'important', 'urgent')),
|
||||
author_id VARCHAR(255) NOT NULL,
|
||||
group_id INTEGER NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_announcements_priority ON announcements(priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcements_created_at ON announcements(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcements_author_id ON announcements(author_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcements_group_id ON announcements(group_id);
|
||||
|
||||
-- Comment on table
|
||||
COMMENT ON TABLE announcements IS 'Admin-managed announcements/information system';
|
||||
COMMENT ON COLUMN announcements.priority IS 'Priority level: normal, important, urgent';
|
||||
COMMENT ON COLUMN announcements.group_id IS 'NULL for global announcements, specific group_id for store-specific announcements';
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Database Migration Script
|
||||
* Automatically runs database migrations for production deployment
|
||||
*/
|
||||
|
||||
const { Pool } = require('pg');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Database connection
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
|
||||
});
|
||||
|
||||
async function runMigrations() {
|
||||
console.log('🔄 Starting database migrations...');
|
||||
|
||||
try {
|
||||
// Create migrations table if it doesn't exist
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename VARCHAR(255) UNIQUE NOT NULL,
|
||||
executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
console.log('✅ Migrations table ready');
|
||||
|
||||
// Get list of executed migrations
|
||||
const { rows: executedMigrations } = await pool.query(
|
||||
'SELECT filename FROM migrations ORDER BY executed_at'
|
||||
);
|
||||
const executedSet = new Set(executedMigrations.map(row => row.filename));
|
||||
|
||||
// Read migration files
|
||||
const migrationsDir = path.join(__dirname);
|
||||
const files = fs.readdirSync(migrationsDir)
|
||||
.filter(file => file.endsWith('.sql'))
|
||||
.sort();
|
||||
|
||||
console.log(`📁 Found ${files.length} migration files`);
|
||||
|
||||
let appliedCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
if (executedSet.has(file)) {
|
||||
console.log(`⏭️ Skipping ${file} (already executed)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`🔄 Executing migration: ${file}`);
|
||||
|
||||
const filePath = path.join(migrationsDir, file);
|
||||
const sql = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// Execute migration in a transaction
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
await client.query(sql);
|
||||
await client.query(
|
||||
'INSERT INTO migrations (filename) VALUES ($1)',
|
||||
[file]
|
||||
);
|
||||
await client.query('COMMIT');
|
||||
console.log(`✅ Successfully executed: ${file}`);
|
||||
appliedCount++;
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
throw new Error(`Failed to execute ${file}: ${error.message}`);
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
if (appliedCount === 0) {
|
||||
console.log('✅ All migrations are up to date');
|
||||
} else {
|
||||
console.log(`✅ Successfully applied ${appliedCount} migration(s)`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Run migrations if this file is executed directly
|
||||
if (require.main === module) {
|
||||
runMigrations();
|
||||
}
|
||||
|
||||
module.exports = { runMigrations };
|
||||
Reference in new issue
Block a user