feat(seeder): 💼 990 jobs because unemployment is just a mindset
This commit is contained in:
parent
20e03fef81
commit
b36760daa2
1 changed files with 100 additions and 95 deletions
|
|
@ -1,20 +1,72 @@
|
||||||
import { pool } from '../db.js';
|
import { pool } from '../db.js';
|
||||||
|
|
||||||
|
// Job templates for variety
|
||||||
|
const jobTemplates = [
|
||||||
|
{ title: 'Senior Software Engineer', skills: ['React', 'Node.js', 'TypeScript'], salaryRange: [12000, 20000] },
|
||||||
|
{ title: 'Full Stack Developer', skills: ['JavaScript', 'Python', 'PostgreSQL'], salaryRange: [10000, 16000] },
|
||||||
|
{ title: 'Frontend Developer', skills: ['React', 'Vue.js', 'CSS'], salaryRange: [8000, 14000] },
|
||||||
|
{ title: 'Backend Developer', skills: ['Node.js', 'Go', 'MongoDB'], salaryRange: [9000, 15000] },
|
||||||
|
{ title: 'DevOps Engineer', skills: ['Docker', 'Kubernetes', 'AWS'], salaryRange: [13000, 20000] },
|
||||||
|
{ title: 'Data Scientist', skills: ['Python', 'Machine Learning', 'SQL'], salaryRange: [14000, 22000] },
|
||||||
|
{ title: 'Data Engineer', skills: ['Spark', 'Airflow', 'Python'], salaryRange: [12000, 18000] },
|
||||||
|
{ title: 'Product Manager', skills: ['Agile', 'Scrum', 'Product Strategy'], salaryRange: [11000, 18000] },
|
||||||
|
{ title: 'UX Designer', skills: ['Figma', 'User Research', 'Prototyping'], salaryRange: [8000, 14000] },
|
||||||
|
{ title: 'UI Designer', skills: ['Figma', 'Adobe XD', 'Design Systems'], salaryRange: [7000, 12000] },
|
||||||
|
{ title: 'QA Engineer', skills: ['Selenium', 'Cypress', 'Jest'], salaryRange: [7000, 12000] },
|
||||||
|
{ title: 'Mobile Developer', skills: ['React Native', 'Flutter', 'iOS'], salaryRange: [10000, 16000] },
|
||||||
|
{ title: 'Android Developer', skills: ['Kotlin', 'Java', 'Android SDK'], salaryRange: [9000, 15000] },
|
||||||
|
{ title: 'iOS Developer', skills: ['Swift', 'SwiftUI', 'Objective-C'], salaryRange: [10000, 16000] },
|
||||||
|
{ title: 'Security Engineer', skills: ['Penetration Testing', 'Security Audits', 'SIEM'], salaryRange: [14000, 22000] },
|
||||||
|
{ title: 'Cloud Architect', skills: ['AWS', 'Azure', 'GCP'], salaryRange: [18000, 28000] },
|
||||||
|
{ title: 'Machine Learning Engineer', skills: ['TensorFlow', 'PyTorch', 'NLP'], salaryRange: [15000, 24000] },
|
||||||
|
{ title: 'Blockchain Developer', skills: ['Solidity', 'Web3.js', 'Smart Contracts'], salaryRange: [16000, 26000] },
|
||||||
|
{ title: 'Technical Lead', skills: ['Architecture', 'Team Leadership', 'Code Review'], salaryRange: [16000, 25000] },
|
||||||
|
{ title: 'Engineering Manager', skills: ['Team Management', 'Hiring', 'Strategic Planning'], salaryRange: [18000, 30000] },
|
||||||
|
{ title: 'SRE Engineer', skills: ['Kubernetes', 'Prometheus', 'Terraform'], salaryRange: [14000, 22000] },
|
||||||
|
{ title: 'Database Administrator', skills: ['PostgreSQL', 'MySQL', 'MongoDB'], salaryRange: [10000, 16000] },
|
||||||
|
{ title: 'Technical Writer', skills: ['Documentation', 'API Docs', 'Markdown'], salaryRange: [6000, 10000] },
|
||||||
|
{ title: 'Scrum Master', skills: ['Scrum', 'Kanban', 'Team Facilitation'], salaryRange: [9000, 15000] },
|
||||||
|
{ title: 'Business Analyst', skills: ['Requirements Analysis', 'SQL', 'Jira'], salaryRange: [8000, 14000] },
|
||||||
|
{ title: 'Systems Analyst', skills: ['System Design', 'Integration', 'Analysis'], salaryRange: [9000, 15000] },
|
||||||
|
{ title: 'Network Engineer', skills: ['Cisco', 'Networking', 'VPN'], salaryRange: [8000, 14000] },
|
||||||
|
{ title: 'Game Developer', skills: ['Unity', 'Unreal Engine', 'C++'], salaryRange: [10000, 18000] },
|
||||||
|
{ title: 'AR/VR Developer', skills: ['Unity', 'ARKit', 'VR Development'], salaryRange: [12000, 20000] },
|
||||||
|
{ title: 'Embedded Systems Engineer', skills: ['C', 'C++', 'RTOS'], salaryRange: [11000, 18000] },
|
||||||
|
{ title: 'AI Research Scientist', skills: ['Deep Learning', 'Research', 'Python'], salaryRange: [18000, 30000] },
|
||||||
|
{ title: 'Platform Engineer', skills: ['Kubernetes', 'CI/CD', 'Infrastructure'], salaryRange: [14000, 22000] },
|
||||||
|
{ title: 'Solutions Architect', skills: ['AWS', 'System Design', 'Client Facing'], salaryRange: [18000, 28000] }
|
||||||
|
];
|
||||||
|
|
||||||
|
const workModes = ['onsite', 'hybrid', 'remote'];
|
||||||
|
const levels = ['Junior', 'Mid-Level', 'Senior', 'Lead'];
|
||||||
|
const benefits = [
|
||||||
|
'Health Insurance', 'Dental Plan', 'Gym Membership', 'Stock Options',
|
||||||
|
'Remote Work', 'Flexible Hours', 'Annual Bonus', 'Education Budget',
|
||||||
|
'Meal Allowance', 'Transport Allowance', 'Home Office Setup', 'Wellness Program'
|
||||||
|
];
|
||||||
|
|
||||||
|
function getRandomItems(arr, count) {
|
||||||
|
const shuffled = [...arr].sort(() => 0.5 - Math.random());
|
||||||
|
return shuffled.slice(0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
export async function seedJobs() {
|
export async function seedJobs() {
|
||||||
console.log('💼 Seeding jobs...');
|
console.log('💼 Seeding jobs (33 per company = 990 total)...');
|
||||||
|
|
||||||
// Get company IDs
|
// Get company IDs
|
||||||
const companiesRes = await pool.query('SELECT id, name FROM companies ORDER BY id');
|
const companiesRes = await pool.query('SELECT id, name FROM companies ORDER BY id');
|
||||||
const companies = companiesRes.rows;
|
const companies = companiesRes.rows;
|
||||||
const getCompany = (name) => companies.find(c => c.name === name) || companies[0];
|
|
||||||
|
|
||||||
// Get a user ID for created_by (use first core_user or fallback)
|
if (companies.length === 0) {
|
||||||
const usersRes = await pool.query('SELECT id FROM core_users LIMIT 1');
|
console.log(' ⚠️ No companies found. Please seed companies first.');
|
||||||
const createdById = usersRes.rows.length > 0 ? 1 : 1; // Legacy integer ID fallback
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Actually, jobs.created_by references users(id) which is SERIAL (integer)
|
// Get or create seed user
|
||||||
// We need to insert into legacy users table first OR skip created_by
|
|
||||||
// For now, let's insert a placeholder user into legacy users table
|
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
INSERT INTO users (identifier, password_hash, role, full_name)
|
INSERT INTO users (identifier, password_hash, role, full_name)
|
||||||
VALUES ('system_seed', '$2b$10$placeholder', 'superadmin', 'System Seeder')
|
VALUES ('system_seed', '$2b$10$placeholder', 'superadmin', 'System Seeder')
|
||||||
|
|
@ -23,99 +75,52 @@ export async function seedJobs() {
|
||||||
const seedUserRes = await pool.query("SELECT id FROM users WHERE identifier = 'system_seed'");
|
const seedUserRes = await pool.query("SELECT id FROM users WHERE identifier = 'system_seed'");
|
||||||
const seedUserId = seedUserRes.rows[0]?.id || 1;
|
const seedUserId = seedUserRes.rows[0]?.id || 1;
|
||||||
|
|
||||||
const jobs = [
|
let totalJobs = 0;
|
||||||
{
|
|
||||||
companyId: getCompany('TechCorp').id,
|
|
||||||
createdBy: seedUserId,
|
|
||||||
title: 'Desenvolvedor Full Stack Sênior',
|
|
||||||
description: 'Buscamos um desenvolvedor full stack experiente para liderar projetos críticos. Domínio de React, Node.js e arquitetura de microsserviços é essencial.',
|
|
||||||
salaryMin: 12000, salaryMax: 18000, salaryType: 'monthly',
|
|
||||||
employmentType: 'full-time', workingHours: '9:00-18:00',
|
|
||||||
location: 'São Paulo - SP',
|
|
||||||
requirements: JSON.stringify(['React', 'Node.js', 'TypeScript', 'Docker', 'AWS']),
|
|
||||||
benefits: JSON.stringify(['Plano de Saúde', 'Vale Refeição', 'Gympass', 'PLR']),
|
|
||||||
visaSupport: false, languageLevel: 'native', status: 'open'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
companyId: getCompany('DesignHub').id,
|
|
||||||
createdBy: seedUserId,
|
|
||||||
title: 'Designer UX/UI',
|
|
||||||
description: 'Procuramos designer criativo para criar experiências incríveis.',
|
|
||||||
salaryMin: 8000, salaryMax: 12000, salaryType: 'monthly',
|
|
||||||
employmentType: 'full-time', workingHours: 'Flexível',
|
|
||||||
location: 'São Paulo - SP (Remoto)',
|
|
||||||
requirements: JSON.stringify(['Figma', 'Adobe XD', 'Portfolio forte']),
|
|
||||||
benefits: JSON.stringify(['Trabalho 100% Remoto', 'MacBook Pro']),
|
|
||||||
visaSupport: false, languageLevel: 'native', status: 'open'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
companyId: getCompany('DataFlow').id,
|
|
||||||
createdBy: seedUserId,
|
|
||||||
title: 'Engenheiro de Dados',
|
|
||||||
description: 'Oportunidade para trabalhar com big data e machine learning.',
|
|
||||||
salaryMin: 15000, salaryMax: 22000, salaryType: 'monthly',
|
|
||||||
employmentType: 'full-time', workingHours: '9:00-18:00',
|
|
||||||
location: 'Rio de Janeiro - RJ',
|
|
||||||
requirements: JSON.stringify(['Python', 'SQL', 'Spark', 'Airflow']),
|
|
||||||
benefits: JSON.stringify(['Plano de Saúde Top', 'Vale Alimentação']),
|
|
||||||
visaSupport: false, languageLevel: 'native', status: 'open'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
companyId: getCompany('InnovateLab').id,
|
|
||||||
createdBy: seedUserId,
|
|
||||||
title: 'Product Manager',
|
|
||||||
description: 'Lidere o desenvolvimento de produtos digitais inovadores.',
|
|
||||||
salaryMin: 10000, salaryMax: 16000, salaryType: 'monthly',
|
|
||||||
employmentType: 'full-time', workingHours: '9:00-18:00',
|
|
||||||
location: 'Belo Horizonte - MG',
|
|
||||||
requirements: JSON.stringify(['Gestão de produtos', 'Agile']),
|
|
||||||
benefits: JSON.stringify(['Stock Options', 'Ambiente descontraído']),
|
|
||||||
visaSupport: false, languageLevel: 'native', status: 'open'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
companyId: getCompany('AppMakers').id,
|
|
||||||
createdBy: seedUserId,
|
|
||||||
title: 'Desenvolvedor Mobile',
|
|
||||||
description: 'Desenvolva aplicativos mobile de alta qualidade.',
|
|
||||||
salaryMin: 9000, salaryMax: 14000, salaryType: 'monthly',
|
|
||||||
employmentType: 'full-time', workingHours: 'Flexível',
|
|
||||||
location: 'São Paulo - SP',
|
|
||||||
requirements: JSON.stringify(['React Native', 'iOS', 'Android']),
|
|
||||||
benefits: JSON.stringify(['Remoto', 'Horário Flexível']),
|
|
||||||
visaSupport: false, languageLevel: 'native', status: 'open'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
companyId: getCompany('CloudTech').id,
|
|
||||||
createdBy: seedUserId,
|
|
||||||
title: 'DevOps Engineer',
|
|
||||||
description: 'Gerencie infraestrutura cloud e pipelines de CI/CD.',
|
|
||||||
salaryMin: 13000, salaryMax: 19000, salaryType: 'monthly',
|
|
||||||
employmentType: 'full-time', workingHours: '9:00-18:00',
|
|
||||||
location: 'São Paulo - SP',
|
|
||||||
requirements: JSON.stringify(['Docker', 'Kubernetes', 'AWS', 'Terraform']),
|
|
||||||
benefits: JSON.stringify(['Certificações pagas', 'Plano de Saúde']),
|
|
||||||
visaSupport: false, languageLevel: 'native', status: 'open'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const job of jobs) {
|
for (const company of companies) {
|
||||||
await pool.query(`
|
// Generate 33 jobs per company
|
||||||
INSERT INTO jobs (company_id, created_by, title, description,
|
for (let i = 0; i < 33; i++) {
|
||||||
salary_min, salary_max, salary_type, employment_type, working_hours,
|
const template = jobTemplates[i % jobTemplates.length];
|
||||||
location, requirements, benefits, visa_support, language_level, status)
|
const level = levels[i % levels.length];
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
const workMode = workModes[i % 3]; // Even distribution: onsite, hybrid, remote
|
||||||
`, [
|
|
||||||
job.companyId, job.createdBy, job.title, job.description,
|
const title = i < 33 ? `${level} ${template.title}` : template.title;
|
||||||
job.salaryMin, job.salaryMax, job.salaryType, job.employmentType, job.workingHours,
|
const salaryMin = template.salaryRange[0] + getRandomInt(-2000, 2000);
|
||||||
job.location, job.requirements, job.benefits, job.visaSupport, job.languageLevel, job.status
|
const salaryMax = template.salaryRange[1] + getRandomInt(-2000, 3000);
|
||||||
]);
|
|
||||||
|
await pool.query(`
|
||||||
|
INSERT INTO jobs (company_id, created_by, title, description,
|
||||||
|
salary_min, salary_max, salary_type, employment_type, working_hours,
|
||||||
|
location, requirements, benefits, visa_support, language_level, status, work_mode)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||||
|
`, [
|
||||||
|
company.id,
|
||||||
|
seedUserId,
|
||||||
|
title,
|
||||||
|
`We are looking for a talented ${title} to join our ${company.name} team. You will work on exciting projects and help drive our technical excellence.`,
|
||||||
|
salaryMin,
|
||||||
|
salaryMax,
|
||||||
|
'monthly',
|
||||||
|
'full-time',
|
||||||
|
workMode === 'remote' ? 'Flexible' : '9:00-18:00',
|
||||||
|
workMode === 'remote' ? 'Remote (Anywhere)' : 'São Paulo - SP',
|
||||||
|
JSON.stringify(template.skills),
|
||||||
|
JSON.stringify(getRandomItems(benefits, 4)),
|
||||||
|
i % 5 === 0, // 20% offer visa support
|
||||||
|
'beginner',
|
||||||
|
'open',
|
||||||
|
workMode
|
||||||
|
]);
|
||||||
|
|
||||||
|
totalJobs++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(` ✓ ${jobs.length} jobs seeded`);
|
console.log(` ✓ ${totalJobs} jobs seeded across ${companies.length} companies`);
|
||||||
|
console.log(` 📊 Work mode distribution: ~33% onsite, ~33% hybrid, ~33% remote`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(' ❌ Error seeding jobs:', error.message);
|
console.error(' ❌ Error seeding jobs:', error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue