3d381944d2
Next.js + Express event management app for Hope Family Church.
134 lines
4.6 KiB
JavaScript
134 lines
4.6 KiB
JavaScript
/**
|
|
* Seed script: inserts 75 demo users (mix of roles, ~25 inactive)
|
|
* Run: node scripts/seed-users.js
|
|
*/
|
|
|
|
const { PrismaClient } = require('@prisma/client');
|
|
const bcrypt = require('bcryptjs');
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
const firstNames = [
|
|
'Liam', 'Olivia', 'Noah', 'Emma', 'James', 'Ava', 'Elijah', 'Sophia',
|
|
'Lucas', 'Isabella', 'Mason', 'Mia', 'Logan', 'Charlotte', 'Ethan',
|
|
'Amelia', 'Aiden', 'Harper', 'Jackson', 'Evelyn', 'Thabo', 'Zanele',
|
|
'Sipho', 'Nomsa', 'Lebo', 'Thandeka', 'Kagiso', 'Palesa', 'Bongani',
|
|
'Ayanda', 'Pieter', 'Anri', 'Francois', 'Elizma', 'Hendrik', 'Marius',
|
|
'Chloé', 'Dylan', 'Amber', 'Caleb', 'Zoe', 'Hunter', 'Lily', 'Owen',
|
|
'Grace', 'Ryan', 'Chloe', 'Nathan', 'Stella', 'Connor', 'Hazel',
|
|
'Miles', 'Violet', 'Eli', 'Aurora', 'Isaiah', 'Luna', 'Nolan',
|
|
'Scarlett', 'Wyatt', 'Penelope', 'Sebastian', 'Layla', 'Asher',
|
|
'Riley', 'Oliver', 'Zoey', 'Ezra', 'Nora', 'Micah', 'Hannah',
|
|
'Jonah', 'Leah', 'Aaron', 'Aubrey', 'Charles',
|
|
];
|
|
|
|
const lastNames = [
|
|
'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller',
|
|
'Davis', 'Wilson', 'Moore', 'Taylor', 'Anderson', 'Thomas', 'Jackson',
|
|
'White', 'Harris', 'Martin', 'Thompson', 'Robinson', 'Lewis',
|
|
'Dlamini', 'Nkosi', 'Mokoena', 'Ndlovu', 'Mthembu', 'Zulu', 'Nxumalo',
|
|
'du Plessis', 'van der Berg', 'Botha', 'Steyn', 'Fourie', 'Pretorius',
|
|
'Venter', 'Coetzee', 'van Niekerk', 'Lombard', 'Janse van Rensburg',
|
|
'Walker', 'Hall', 'Allen', 'Young', 'Hernandez', 'King', 'Wright',
|
|
'Scott', 'Green', 'Baker', 'Adams', 'Nelson', 'Carter', 'Mitchell',
|
|
'Perez', 'Roberts', 'Turner', 'Phillips', 'Campbell', 'Parker', 'Evans',
|
|
'Edwards', 'Collins', 'Stewart', 'Morris', 'Reed', 'Cook', 'Morgan',
|
|
];
|
|
|
|
function pick(arr) {
|
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
}
|
|
|
|
function randomPhone() {
|
|
// SA mobile format: 07x/08x xxxxxxx
|
|
const prefixes = ['071', '072', '073', '074', '076', '078', '079', '081', '082', '083', '084'];
|
|
return `${pick(prefixes)}${String(Math.floor(Math.random() * 9000000) + 1000000)}`;
|
|
}
|
|
|
|
async function main() {
|
|
const passwordHash = await bcrypt.hash('Demo@1234', 10);
|
|
|
|
// Role distribution: ~55 user, ~15 staff, ~5 supervisor
|
|
const rolePool = [
|
|
...Array(55).fill('user'),
|
|
...Array(15).fill('staff'),
|
|
...Array(5).fill('supervisor'),
|
|
];
|
|
|
|
const notifPool = ['email', 'email', 'email', 'whatsapp', 'both'];
|
|
|
|
const usedEmails = new Set();
|
|
const usedPhones = new Set();
|
|
const users = [];
|
|
|
|
// Build 75 user records
|
|
while (users.length < 75) {
|
|
const first = pick(firstNames);
|
|
const last = pick(lastNames);
|
|
const tag = Math.floor(Math.random() * 999) + 1;
|
|
const email = `${first.toLowerCase().replace(/ /g, '')}.${last.toLowerCase().replace(/ /g, '').replace(/'/g, '')}${tag}@demo.church`;
|
|
|
|
if (usedEmails.has(email)) continue;
|
|
usedEmails.add(email);
|
|
|
|
// ~60% of users have a phone number
|
|
let phone = null;
|
|
if (Math.random() < 0.6) {
|
|
let attempt = randomPhone();
|
|
let tries = 0;
|
|
while (usedPhones.has(attempt) && tries < 20) {
|
|
attempt = randomPhone();
|
|
tries++;
|
|
}
|
|
if (!usedPhones.has(attempt)) {
|
|
usedPhones.add(attempt);
|
|
phone = attempt;
|
|
}
|
|
}
|
|
|
|
users.push({
|
|
name: `${first} ${last}`,
|
|
email,
|
|
password: passwordHash,
|
|
role: pick(rolePool),
|
|
isActive: true, // will override 25 below
|
|
phoneNumber: phone,
|
|
notificationPreference: pick(notifPool),
|
|
});
|
|
}
|
|
|
|
// Mark exactly 25 as inactive (spread across the list)
|
|
const inactiveIndexes = new Set();
|
|
while (inactiveIndexes.size < 25) {
|
|
inactiveIndexes.add(Math.floor(Math.random() * 75));
|
|
}
|
|
inactiveIndexes.forEach(i => { users[i].isActive = false; });
|
|
|
|
console.log(`Inserting ${users.length} users…`);
|
|
console.log(` Active: ${users.filter(u => u.isActive).length}`);
|
|
console.log(` Inactive: ${users.filter(u => !u.isActive).length}`);
|
|
console.log(` Roles: user=${users.filter(u => u.role === 'user').length}, staff=${users.filter(u => u.role === 'staff').length}, supervisor=${users.filter(u => u.role === 'supervisor').length}`);
|
|
|
|
let created = 0;
|
|
let skipped = 0;
|
|
|
|
for (const user of users) {
|
|
try {
|
|
await prisma.user.create({ data: user });
|
|
created++;
|
|
} catch (err) {
|
|
if (err.code === 'P2002') {
|
|
skipped++;
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Done. Created: ${created}, Skipped (duplicate): ${skipped}`);
|
|
console.log('Default password for all demo users: Demo@1234');
|
|
}
|
|
|
|
main()
|
|
.catch(err => { console.error(err); process.exit(1); })
|
|
.finally(() => prisma.$disconnect()); |