From 9e37ea3bc61a7ac1f027195b1775eb21013fbe52 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 Aug 2025 08:38:54 +0000 Subject: [PATCH] Add MongoDB integration with models, API routes, and seed data Co-authored-by: nitesh.kpoddar15 --- MONGODB_SETUP.md | 286 +++++++++++++++++++++++ app/api/industries/route.js | 43 ++++ app/api/jobs/route.js | 49 ++++ app/api/products/route.js | 45 ++++ app/api/training/route.js | 45 ++++ lib/data-operations.js | 239 +++++++++++++++++++ lib/models/Industry.js | 41 ++++ lib/models/JobPosting.js | 63 +++++ lib/models/Product.js | 44 ++++ lib/models/TrainingCourse.js | 64 ++++++ lib/mongodb.js | 29 +++ lib/mongoose.js | 47 ++++ package-lock.json | 216 +++++++++++++++++- package.json | 6 +- scripts/seed-data.js | 431 +++++++++++++++++++++++++++++++++++ 15 files changed, 1643 insertions(+), 5 deletions(-) create mode 100644 MONGODB_SETUP.md create mode 100644 app/api/industries/route.js create mode 100644 app/api/jobs/route.js create mode 100644 app/api/products/route.js create mode 100644 app/api/training/route.js create mode 100644 lib/data-operations.js create mode 100644 lib/models/Industry.js create mode 100644 lib/models/JobPosting.js create mode 100644 lib/models/Product.js create mode 100644 lib/models/TrainingCourse.js create mode 100644 lib/mongodb.js create mode 100644 lib/mongoose.js create mode 100644 scripts/seed-data.js diff --git a/MONGODB_SETUP.md b/MONGODB_SETUP.md new file mode 100644 index 0000000..9d67684 --- /dev/null +++ b/MONGODB_SETUP.md @@ -0,0 +1,286 @@ +# MongoDB Atlas Setup Guide for Welding Industry Website + +This guide will help you set up MongoDB Atlas and populate your welding industry website with data. + +## Prerequisites + +- MongoDB Atlas account (free tier available) +- Node.js and npm installed +- Next.js project setup + +## Step 1: Create MongoDB Atlas Cluster + +1. Go to [MongoDB Atlas](https://www.mongodb.com/atlas) and create a free account +2. Create a new cluster: + - Choose the free tier (M0) + - Select your preferred cloud provider and region + - Name your cluster (e.g., "welding-company") + +3. Set up database access: + - Go to "Database Access" in the left sidebar + - Click "Add New Database User" + - Choose "Password" authentication + - Create a username and strong password + - Set database user privileges to "Read and write to any database" + +4. Set up network access: + - Go to "Network Access" in the left sidebar + - Click "Add IP Address" + - For development, you can add "0.0.0.0/0" (allow access from anywhere) + - For production, use specific IP addresses + +## Step 2: Get Connection String + +1. Go to "Clusters" and click "Connect" on your cluster +2. Choose "Connect your application" +3. Select "Node.js" as driver and version "4.1 or later" +4. Copy the connection string (it looks like): + ``` + mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority + ``` + +## Step 3: Configure Environment Variables + +1. Update your `.env.local` file with your actual MongoDB connection string: + ```env + MONGODB_URI=mongodb+srv://yourusername:yourpassword@cluster0.xxxxx.mongodb.net/welding-company?retryWrites=true&w=majority + ``` + + Replace: + - `yourusername` with your database username + - `yourpassword` with your database password + - `cluster0.xxxxx` with your actual cluster connection string + - `welding-company` with your preferred database name + +## Step 4: Install Dependencies + +The required packages are already installed: +- `mongodb` - MongoDB driver +- `mongoose` - ODM for MongoDB +- `dotenv` - Environment variable loader + +## Step 5: Populate Database with Sample Data + +Run the seeding script to populate your database with sample welding industry data: + +```bash +npm run seed +``` + +This will create sample data for: +- **Products**: Welding equipment, safety gear, consumables +- **Industries**: Automotive, Construction, Oil & Gas +- **Training Courses**: MIG welding, TIG welding, Inspector certification +- **Job Postings**: Various welding positions + +## Step 6: Verify Database Setup + +You can verify the setup by: + +1. Checking MongoDB Atlas dashboard to see your collections +2. Testing API endpoints: + ```bash + # Start your development server + npm run dev + + # Test endpoints (in another terminal or browser) + curl http://localhost:3000/api/products + curl http://localhost:3000/api/industries + curl http://localhost:3000/api/training + curl http://localhost:3000/api/jobs + ``` + +## Database Schema Overview + +### Products Collection +- **Fields**: name, category, description, specifications, price, images, inStock, featured, tags +- **Categories**: welding-equipment, consumables, safety-gear, accessories + +### Industries Collection +- **Fields**: name, slug, description, services, applications, image, caseStudies, featured + +### Training Courses Collection +- **Fields**: title, slug, description, level, duration, price, curriculum, prerequisites, certification, instructor, schedule, featured + +### Job Postings Collection +- **Fields**: title, department, location, type, experience, salary, description, responsibilities, requirements, benefits, skills, isActive, featured + +## API Endpoints + +### Products +- `GET /api/products` - Get all products +- `GET /api/products?category=welding-equipment` - Filter by category +- `GET /api/products?featured=true` - Get featured products +- `POST /api/products` - Create new product + +### Industries +- `GET /api/industries` - Get all industries +- `GET /api/industries?featured=true` - Get featured industries +- `POST /api/industries` - Create new industry + +### Training +- `GET /api/training` - Get all courses +- `GET /api/training?level=beginner` - Filter by level +- `GET /api/training?featured=true` - Get featured courses +- `POST /api/training` - Create new course + +### Jobs +- `GET /api/jobs` - Get all active jobs +- `GET /api/jobs?department=Engineering` - Filter by department +- `GET /api/jobs?featured=true` - Get featured jobs +- `POST /api/jobs` - Create new job posting + +## Using Data in Your Components + +Here's how to fetch and use data in your Next.js components: + +### Example: Fetching Products + +```jsx +// In a page or component +import { useEffect, useState } from 'react' + +export default function ProductsPage() { + const [products, setProducts] = useState([]) + const [loading, setLoading] = useState(true) + + useEffect(() => { + const fetchProducts = async () => { + try { + const response = await fetch('/api/products?featured=true') + const data = await response.json() + if (data.success) { + setProducts(data.data) + } + } catch (error) { + console.error('Error fetching products:', error) + } finally { + setLoading(false) + } + } + + fetchProducts() + }, []) + + if (loading) return
Loading...
+ + return ( +
+ {products.map((product) => ( +
+

{product.name}

+

{product.description}

+

${product.price}

+
+ ))} +
+ ) +} +``` + +### Example: Server-Side Rendering + +```jsx +// For server-side rendering (in pages/ directory) +import { productOperations } from '@/lib/data-operations' + +export async function getServerSideProps() { + try { + const featuredProducts = await productOperations.getFeatured(6) + + return { + props: { + products: JSON.parse(JSON.stringify(featuredProducts)) + } + } + } catch (error) { + return { + props: { + products: [] + } + } + } +} + +export default function HomePage({ products }) { + return ( +
+

Featured Products

+ {/* Render products */} +
+ ) +} +``` + +## Data Management Utilities + +Use the provided data operations for common tasks: + +```javascript +import { + productOperations, + industryOperations, + trainingOperations, + jobOperations +} from '@/lib/data-operations' + +// Examples +const featuredProducts = await productOperations.getFeatured(4) +const weldingEquipment = await productOperations.getByCategory('welding-equipment') +const beginnerCourses = await trainingOperations.getByLevel('beginner') +const engineeringJobs = await jobOperations.getByDepartment('Engineering') +``` + +## Production Considerations + +1. **Security**: + - Use specific IP addresses in Network Access for production + - Create separate database users for different environments + - Use strong passwords and rotate them regularly + +2. **Performance**: + - Add database indexes for frequently queried fields + - Use pagination for large result sets + - Implement caching strategies + +3. **Monitoring**: + - Set up MongoDB Atlas monitoring and alerts + - Monitor API endpoint performance + - Log database operations + +## Troubleshooting + +### Common Issues: + +1. **Connection Error**: Check your connection string and network access settings +2. **Authentication Failed**: Verify username/password and database user permissions +3. **No Data**: Run the seed script again or check if the data was created in MongoDB Atlas + +### Useful Commands: + +```bash +# Re-seed database +npm run seed + +# Check MongoDB connection +node -e "require('dotenv').config({path:'.env.local'}); console.log(process.env.MONGODB_URI)" + +# Test database connection +node -e " +require('dotenv').config({path:'.env.local'}); +const mongoose = require('mongoose'); +mongoose.connect(process.env.MONGODB_URI) + .then(() => console.log('✅ Connected to MongoDB')) + .catch(err => console.error('❌ Connection failed:', err)) +" +``` + +## Next Steps + +1. Customize the data models to match your specific business requirements +2. Add validation and error handling to your API endpoints +3. Implement user authentication and authorization +4. Add search and filtering capabilities +5. Set up automated backups for production data + +For questions or issues, refer to the [MongoDB Atlas Documentation](https://docs.atlas.mongodb.com/) or [Mongoose Documentation](https://mongoosejs.com/). \ No newline at end of file diff --git a/app/api/industries/route.js b/app/api/industries/route.js new file mode 100644 index 0000000..2a14224 --- /dev/null +++ b/app/api/industries/route.js @@ -0,0 +1,43 @@ +import { NextResponse } from 'next/server' +import dbConnect from '@/lib/mongoose' +import Industry from '@/lib/models/Industry' + +export async function GET(request) { + try { + await dbConnect() + + const { searchParams } = new URL(request.url) + const featured = searchParams.get('featured') + const limit = parseInt(searchParams.get('limit')) || 0 + + let query = {} + if (featured === 'true') query.featured = true + + const industries = await Industry.find(query) + .limit(limit) + .sort({ createdAt: -1 }) + + return NextResponse.json({ success: true, data: industries }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} + +export async function POST(request) { + try { + await dbConnect() + + const body = await request.json() + const industry = await Industry.create(body) + + return NextResponse.json({ success: true, data: industry }, { status: 201 }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 400 } + ) + } +} \ No newline at end of file diff --git a/app/api/jobs/route.js b/app/api/jobs/route.js new file mode 100644 index 0000000..68ce112 --- /dev/null +++ b/app/api/jobs/route.js @@ -0,0 +1,49 @@ +import { NextResponse } from 'next/server' +import dbConnect from '@/lib/mongoose' +import JobPosting from '@/lib/models/JobPosting' + +export async function GET(request) { + try { + await dbConnect() + + const { searchParams } = new URL(request.url) + const department = searchParams.get('department') + const type = searchParams.get('type') + const featured = searchParams.get('featured') + const active = searchParams.get('active') + const limit = parseInt(searchParams.get('limit')) || 0 + + let query = {} + if (department) query.department = department + if (type) query.type = type + if (featured === 'true') query.featured = true + if (active !== null) query.isActive = active === 'true' + + const jobs = await JobPosting.find(query) + .limit(limit) + .sort({ createdAt: -1 }) + + return NextResponse.json({ success: true, data: jobs }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} + +export async function POST(request) { + try { + await dbConnect() + + const body = await request.json() + const job = await JobPosting.create(body) + + return NextResponse.json({ success: true, data: job }, { status: 201 }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 400 } + ) + } +} \ No newline at end of file diff --git a/app/api/products/route.js b/app/api/products/route.js new file mode 100644 index 0000000..db4b8a6 --- /dev/null +++ b/app/api/products/route.js @@ -0,0 +1,45 @@ +import { NextResponse } from 'next/server' +import dbConnect from '@/lib/mongoose' +import Product from '@/lib/models/Product' + +export async function GET(request) { + try { + await dbConnect() + + const { searchParams } = new URL(request.url) + const category = searchParams.get('category') + const featured = searchParams.get('featured') + const limit = parseInt(searchParams.get('limit')) || 0 + + let query = {} + if (category) query.category = category + if (featured === 'true') query.featured = true + + const products = await Product.find(query) + .limit(limit) + .sort({ createdAt: -1 }) + + return NextResponse.json({ success: true, data: products }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} + +export async function POST(request) { + try { + await dbConnect() + + const body = await request.json() + const product = await Product.create(body) + + return NextResponse.json({ success: true, data: product }, { status: 201 }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 400 } + ) + } +} \ No newline at end of file diff --git a/app/api/training/route.js b/app/api/training/route.js new file mode 100644 index 0000000..69098aa --- /dev/null +++ b/app/api/training/route.js @@ -0,0 +1,45 @@ +import { NextResponse } from 'next/server' +import dbConnect from '@/lib/mongoose' +import TrainingCourse from '@/lib/models/TrainingCourse' + +export async function GET(request) { + try { + await dbConnect() + + const { searchParams } = new URL(request.url) + const level = searchParams.get('level') + const featured = searchParams.get('featured') + const limit = parseInt(searchParams.get('limit')) || 0 + + let query = {} + if (level) query.level = level + if (featured === 'true') query.featured = true + + const courses = await TrainingCourse.find(query) + .limit(limit) + .sort({ createdAt: -1 }) + + return NextResponse.json({ success: true, data: courses }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} + +export async function POST(request) { + try { + await dbConnect() + + const body = await request.json() + const course = await TrainingCourse.create(body) + + return NextResponse.json({ success: true, data: course }, { status: 201 }) + } catch (error) { + return NextResponse.json( + { success: false, error: error.message }, + { status: 400 } + ) + } +} \ No newline at end of file diff --git a/lib/data-operations.js b/lib/data-operations.js new file mode 100644 index 0000000..bba7873 --- /dev/null +++ b/lib/data-operations.js @@ -0,0 +1,239 @@ +import dbConnect from './mongoose' +import Product from './models/Product' +import Industry from './models/Industry' +import TrainingCourse from './models/TrainingCourse' +import JobPosting from './models/JobPosting' + +// Product operations +export const productOperations = { + // Get all products with optional filters + async getAll(filters = {}) { + await dbConnect() + return await Product.find(filters).sort({ createdAt: -1 }) + }, + + // Get featured products + async getFeatured(limit = 4) { + await dbConnect() + return await Product.find({ featured: true }).limit(limit) + }, + + // Get products by category + async getByCategory(category, limit = 0) { + await dbConnect() + return await Product.find({ category }).limit(limit) + }, + + // Create new product + async create(productData) { + await dbConnect() + return await Product.create(productData) + }, + + // Update product + async update(id, updateData) { + await dbConnect() + return await Product.findByIdAndUpdate(id, updateData, { new: true }) + }, + + // Delete product + async delete(id) { + await dbConnect() + return await Product.findByIdAndDelete(id) + } +} + +// Industry operations +export const industryOperations = { + // Get all industries + async getAll() { + await dbConnect() + return await Industry.find({}).sort({ createdAt: -1 }) + }, + + // Get featured industries + async getFeatured(limit = 3) { + await dbConnect() + return await Industry.find({ featured: true }).limit(limit) + }, + + // Get industry by slug + async getBySlug(slug) { + await dbConnect() + return await Industry.findOne({ slug }) + }, + + // Create new industry + async create(industryData) { + await dbConnect() + return await Industry.create(industryData) + }, + + // Update industry + async update(id, updateData) { + await dbConnect() + return await Industry.findByIdAndUpdate(id, updateData, { new: true }) + } +} + +// Training course operations +export const trainingOperations = { + // Get all courses with optional filters + async getAll(filters = {}) { + await dbConnect() + return await TrainingCourse.find(filters).sort({ createdAt: -1 }) + }, + + // Get featured courses + async getFeatured(limit = 3) { + await dbConnect() + return await TrainingCourse.find({ featured: true }).limit(limit) + }, + + // Get courses by level + async getByLevel(level) { + await dbConnect() + return await TrainingCourse.find({ level }) + }, + + // Get course by slug + async getBySlug(slug) { + await dbConnect() + return await TrainingCourse.findOne({ slug }) + }, + + // Create new course + async create(courseData) { + await dbConnect() + return await TrainingCourse.create(courseData) + }, + + // Update course + async update(id, updateData) { + await dbConnect() + return await TrainingCourse.findByIdAndUpdate(id, updateData, { new: true }) + }, + + // Enroll student in course schedule + async enrollStudent(courseId, scheduleIndex) { + await dbConnect() + const course = await TrainingCourse.findById(courseId) + if (course && course.schedule[scheduleIndex]) { + course.schedule[scheduleIndex].enrolledStudents += 1 + return await course.save() + } + throw new Error('Course or schedule not found') + } +} + +// Job posting operations +export const jobOperations = { + // Get all active jobs + async getActive(filters = {}) { + await dbConnect() + return await JobPosting.find({ isActive: true, ...filters }).sort({ createdAt: -1 }) + }, + + // Get featured jobs + async getFeatured(limit = 3) { + await dbConnect() + return await JobPosting.find({ featured: true, isActive: true }).limit(limit) + }, + + // Get jobs by department + async getByDepartment(department) { + await dbConnect() + return await JobPosting.find({ department, isActive: true }) + }, + + // Get jobs by type + async getByType(type) { + await dbConnect() + return await JobPosting.find({ type, isActive: true }) + }, + + // Create new job posting + async create(jobData) { + await dbConnect() + return await JobPosting.create(jobData) + }, + + // Update job posting + async update(id, updateData) { + await dbConnect() + return await JobPosting.findByIdAndUpdate(id, updateData, { new: true }) + }, + + // Deactivate job posting + async deactivate(id) { + await dbConnect() + return await JobPosting.findByIdAndUpdate(id, { isActive: false }, { new: true }) + } +} + +// General utility functions +export const generalOperations = { + // Get dashboard stats + async getDashboardStats() { + await dbConnect() + + const [productCount, industryCount, courseCount, activeJobCount] = await Promise.all([ + Product.countDocuments(), + Industry.countDocuments(), + TrainingCourse.countDocuments(), + JobPosting.countDocuments({ isActive: true }) + ]) + + return { + products: productCount, + industries: industryCount, + courses: courseCount, + activeJobs: activeJobCount + } + }, + + // Search across all collections + async globalSearch(searchTerm) { + await dbConnect() + + const searchRegex = new RegExp(searchTerm, 'i') + + const [products, industries, courses, jobs] = await Promise.all([ + Product.find({ + $or: [ + { name: searchRegex }, + { description: searchRegex }, + { tags: { $in: [searchRegex] } } + ] + }).limit(5), + Industry.find({ + $or: [ + { name: searchRegex }, + { description: searchRegex }, + { services: { $in: [searchRegex] } } + ] + }).limit(5), + TrainingCourse.find({ + $or: [ + { title: searchRegex }, + { description: searchRegex } + ] + }).limit(5), + JobPosting.find({ + isActive: true, + $or: [ + { title: searchRegex }, + { description: searchRegex }, + { skills: { $in: [searchRegex] } } + ] + }).limit(5) + ]) + + return { + products, + industries, + courses, + jobs + } + } +} \ No newline at end of file diff --git a/lib/models/Industry.js b/lib/models/Industry.js new file mode 100644 index 0000000..53a0be5 --- /dev/null +++ b/lib/models/Industry.js @@ -0,0 +1,41 @@ +import mongoose from 'mongoose' + +const IndustrySchema = new mongoose.Schema({ + name: { + type: String, + required: true, + trim: true + }, + slug: { + type: String, + required: true, + unique: true + }, + description: { + type: String, + required: true + }, + services: [{ + type: String + }], + applications: [{ + type: String + }], + image: { + type: String + }, + caseStudies: [{ + title: String, + description: String, + image: String, + results: String + }], + featured: { + type: Boolean, + default: false + } +}, { + timestamps: true +}) + +export default mongoose.models.Industry || mongoose.model('Industry', IndustrySchema) \ No newline at end of file diff --git a/lib/models/JobPosting.js b/lib/models/JobPosting.js new file mode 100644 index 0000000..9548345 --- /dev/null +++ b/lib/models/JobPosting.js @@ -0,0 +1,63 @@ +import mongoose from 'mongoose' + +const JobPostingSchema = new mongoose.Schema({ + title: { + type: String, + required: true, + trim: true + }, + department: { + type: String, + required: true + }, + location: { + type: String, + required: true + }, + type: { + type: String, + required: true, + enum: ['full-time', 'part-time', 'contract', 'temporary'] + }, + experience: { + type: String, + required: true, + enum: ['entry-level', '1-3 years', '3-5 years', '5+ years'] + }, + salary: { + min: Number, + max: Number, + currency: { type: String, default: 'USD' } + }, + description: { + type: String, + required: true + }, + responsibilities: [{ + type: String + }], + requirements: [{ + type: String + }], + benefits: [{ + type: String + }], + skills: [{ + type: String + }], + isActive: { + type: Boolean, + default: true + }, + applicationDeadline: { + type: Date + }, + featured: { + type: Boolean, + default: false + } +}, { + timestamps: true +}) + +export default mongoose.models.JobPosting || mongoose.model('JobPosting', JobPostingSchema) \ No newline at end of file diff --git a/lib/models/Product.js b/lib/models/Product.js new file mode 100644 index 0000000..28d9904 --- /dev/null +++ b/lib/models/Product.js @@ -0,0 +1,44 @@ +import mongoose from 'mongoose' + +const ProductSchema = new mongoose.Schema({ + name: { + type: String, + required: true, + trim: true + }, + category: { + type: String, + required: true, + enum: ['welding-equipment', 'consumables', 'safety-gear', 'accessories'] + }, + description: { + type: String, + required: true + }, + specifications: { + type: Object, + default: {} + }, + price: { + type: Number, + required: true + }, + images: [{ + type: String + }], + inStock: { + type: Boolean, + default: true + }, + featured: { + type: Boolean, + default: false + }, + tags: [{ + type: String + }] +}, { + timestamps: true +}) + +export default mongoose.models.Product || mongoose.model('Product', ProductSchema) \ No newline at end of file diff --git a/lib/models/TrainingCourse.js b/lib/models/TrainingCourse.js new file mode 100644 index 0000000..a4d406b --- /dev/null +++ b/lib/models/TrainingCourse.js @@ -0,0 +1,64 @@ +import mongoose from 'mongoose' + +const TrainingCourseSchema = new mongoose.Schema({ + title: { + type: String, + required: true, + trim: true + }, + slug: { + type: String, + required: true, + unique: true + }, + description: { + type: String, + required: true + }, + level: { + type: String, + required: true, + enum: ['beginner', 'intermediate', 'advanced'] + }, + duration: { + type: String, + required: true + }, + price: { + type: Number, + required: true + }, + curriculum: [{ + module: String, + topics: [String] + }], + prerequisites: [{ + type: String + }], + certification: { + type: String + }, + instructor: { + name: String, + bio: String, + image: String + }, + schedule: [{ + startDate: Date, + endDate: Date, + location: String, + maxStudents: Number, + enrolledStudents: { type: Number, default: 0 } + }], + image: { + type: String + }, + featured: { + type: Boolean, + default: false + } +}, { + timestamps: true +}) + +export default mongoose.models.TrainingCourse || mongoose.model('TrainingCourse', TrainingCourseSchema) \ No newline at end of file diff --git a/lib/mongodb.js b/lib/mongodb.js new file mode 100644 index 0000000..6e56eed --- /dev/null +++ b/lib/mongodb.js @@ -0,0 +1,29 @@ +import { MongoClient } from 'mongodb' + +const uri = process.env.MONGODB_URI +const options = {} + +let client +let clientPromise + +if (!process.env.MONGODB_URI) { + throw new Error('Please add your Mongo URI to .env.local') +} + +if (process.env.NODE_ENV === 'development') { + // In development mode, use a global variable so that the value + // is preserved across module reloads caused by HMR (Hot Module Replacement). + if (!global._mongoClientPromise) { + client = new MongoClient(uri, options) + global._mongoClientPromise = client.connect() + } + clientPromise = global._mongoClientPromise +} else { + // In production mode, it's best to not use a global variable. + client = new MongoClient(uri, options) + clientPromise = client.connect() +} + +// Export a module-scoped MongoClient promise. By doing this in a +// separate module, the client can be shared across functions. +export default clientPromise \ No newline at end of file diff --git a/lib/mongoose.js b/lib/mongoose.js new file mode 100644 index 0000000..8c2065f --- /dev/null +++ b/lib/mongoose.js @@ -0,0 +1,47 @@ +import mongoose from 'mongoose' + +const MONGODB_URI = process.env.MONGODB_URI + +if (!MONGODB_URI) { + throw new Error( + 'Please define the MONGODB_URI environment variable inside .env.local' + ) +} + +/** + * Global is used here to maintain a cached connection across hot reloads + * in development. This prevents connections growing exponentially + * during API Route usage. + */ +let cached = global.mongoose + +if (!cached) { + cached = global.mongoose = { conn: null, promise: null } +} + +async function dbConnect() { + if (cached.conn) { + return cached.conn + } + + if (!cached.promise) { + const opts = { + bufferCommands: false, + } + + cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => { + return mongoose + }) + } + + try { + cached.conn = await cached.promise + } catch (e) { + cached.promise = null + throw e + } + + return cached.conn +} + +export default dbConnect \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5cd9983..8b69f91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.1.0", "dependencies": { "@heroicons/react": "^2.2.0", + "dotenv": "^17.2.1", + "mongodb": "^6.18.0", + "mongoose": "^8.17.0", "next": "^14.2.30", "nodemailer": "^6.10.1", "react": "^18", @@ -258,6 +261,15 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", @@ -589,6 +601,21 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", @@ -1406,6 +1433,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -1691,7 +1727,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -1783,6 +1818,18 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3508,6 +3555,15 @@ "node": ">=4.0" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3616,6 +3672,12 @@ "node": ">= 0.4" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3671,11 +3733,109 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mongodb": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", + "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.0.tgz", + "integrity": "sha512-mxW6TBPHViORfNYOFXCVOnT4d5aRr+CgDxTs1ViYXfuHzNpkelgJQrQa+Lz6hofoEQISnKlXv1L3ZnHyJRkhfA==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.18.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mz": { "version": "2.7.0", @@ -4272,7 +4432,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -4689,6 +4848,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -4720,6 +4885,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -5275,6 +5449,18 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -5529,6 +5715,28 @@ "dev": true, "license": "MIT" }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 686abdb..77feb2f 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,14 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "seed": "node scripts/seed-data.js" }, "dependencies": { "@heroicons/react": "^2.2.0", + "dotenv": "^17.2.1", + "mongodb": "^6.18.0", + "mongoose": "^8.17.0", "next": "^14.2.30", "nodemailer": "^6.10.1", "react": "^18", diff --git a/scripts/seed-data.js b/scripts/seed-data.js new file mode 100644 index 0000000..b76383a --- /dev/null +++ b/scripts/seed-data.js @@ -0,0 +1,431 @@ +require('dotenv').config({ path: '.env.local' }) +const mongoose = require('mongoose') + +// Import models +const Product = require('../lib/models/Product.js').default +const Industry = require('../lib/models/Industry.js').default +const TrainingCourse = require('../lib/models/TrainingCourse.js').default +const JobPosting = require('../lib/models/JobPosting.js').default + +const seedData = async () => { + try { + // Connect to MongoDB + await mongoose.connect(process.env.MONGODB_URI) + console.log('Connected to MongoDB') + + // Clear existing data + await Product.deleteMany({}) + await Industry.deleteMany({}) + await TrainingCourse.deleteMany({}) + await JobPosting.deleteMany({}) + console.log('Cleared existing data') + + // Seed Products + const products = [ + { + name: "MIG-250 Professional Welder", + category: "welding-equipment", + description: "High-performance MIG welder suitable for industrial applications. Features digital display, precise arc control, and robust construction.", + specifications: { + amperage: "30-250A", + voltage: "220V/440V", + duty_cycle: "60% at 250A", + weight: "85 lbs" + }, + price: 2499.99, + images: ["/images/mig-250.jpg"], + featured: true, + tags: ["MIG", "professional", "industrial"] + }, + { + name: "TIG-200 Precision Welder", + category: "welding-equipment", + description: "Precision TIG welder perfect for detailed work on stainless steel and aluminum. AC/DC capability with pulse control.", + specifications: { + amperage: "5-200A", + voltage: "220V", + duty_cycle: "35% at 200A", + weight: "45 lbs" + }, + price: 1899.99, + images: ["/images/tig-200.jpg"], + featured: true, + tags: ["TIG", "precision", "aluminum"] + }, + { + name: "Premium Safety Helmet", + category: "safety-gear", + description: "Auto-darkening welding helmet with panoramic view and multiple arc sensors for optimal protection.", + specifications: { + shade_range: "9-13", + switching_time: "1/25000s", + sensor_count: 4, + weight: "1.8 lbs" + }, + price: 299.99, + images: ["/images/safety-helmet.jpg"], + featured: false, + tags: ["safety", "auto-darkening", "protection"] + }, + { + name: "ER70S-6 Welding Wire", + category: "consumables", + description: "High-quality carbon steel welding wire for MIG welding. Excellent arc stability and minimal spatter.", + specifications: { + diameter: "0.035 inch", + weight: "44 lbs", + material: "Carbon Steel", + aws_class: "ER70S-6" + }, + price: 89.99, + images: ["/images/welding-wire.jpg"], + featured: false, + tags: ["consumables", "MIG", "carbon-steel"] + } + ] + + const createdProducts = await Product.insertMany(products) + console.log(`Seeded ${createdProducts.length} products`) + + // Seed Industries + const industries = [ + { + name: "Automotive Manufacturing", + slug: "automotive-manufacturing", + description: "Comprehensive welding solutions for automotive assembly lines, chassis fabrication, and component manufacturing.", + services: [ + "Assembly line welding automation", + "Chassis and frame welding", + "Exhaust system fabrication", + "Body panel joining", + "Quality control and inspection" + ], + applications: [ + "Robotic welding systems", + "Spot welding for body assembly", + "MIG welding for structural components", + "Laser welding for precision parts" + ], + image: "/images/automotive-industry.jpg", + featured: true, + caseStudies: [ + { + title: "Major Auto Plant Automation", + description: "Implemented robotic welding system for SUV production line", + results: "40% increase in production efficiency" + } + ] + }, + { + name: "Construction & Infrastructure", + slug: "construction-infrastructure", + description: "Heavy-duty welding services for bridges, buildings, and infrastructure projects requiring certified structural welding.", + services: [ + "Structural steel welding", + "Bridge construction", + "High-rise building framework", + "Pipeline installation", + "Certified welding procedures" + ], + applications: [ + "Arc welding for structural steel", + "Flux-cored welding for outdoor projects", + "Stick welding for heavy sections", + "Submerged arc welding for thick materials" + ], + image: "/images/construction-industry.jpg", + featured: true, + caseStudies: [ + { + title: "Downtown Bridge Project", + description: "Complete welding services for 500ft steel bridge construction", + results: "Project completed 2 weeks ahead of schedule" + } + ] + }, + { + name: "Oil & Gas", + slug: "oil-gas", + description: "Specialized welding services for oil refineries, pipelines, and offshore platforms with strict safety and quality standards.", + services: [ + "Pipeline welding and repair", + "Pressure vessel fabrication", + "Offshore platform construction", + "Refinery maintenance", + "API certified procedures" + ], + applications: [ + "Pipeline welding with X-ray testing", + "Stainless steel for chemical processing", + "Underwater welding capabilities", + "High-pressure system fabrication" + ], + image: "/images/oil-gas-industry.jpg", + featured: true + } + ] + + const createdIndustries = await Industry.insertMany(industries) + console.log(`Seeded ${createdIndustries.length} industries`) + + // Seed Training Courses + const trainingCourses = [ + { + title: "Basic MIG Welding Fundamentals", + slug: "basic-mig-welding", + description: "Learn the fundamentals of MIG welding including safety, equipment setup, and basic techniques. Perfect for beginners entering the welding field.", + level: "beginner", + duration: "40 hours (1 week)", + price: 899.99, + curriculum: [ + { + module: "Safety and PPE", + topics: ["Welding safety fundamentals", "Personal protective equipment", "Workshop safety procedures"] + }, + { + module: "Equipment Basics", + topics: ["MIG welder components", "Gas selection", "Wire selection", "Equipment maintenance"] + }, + { + module: "Welding Techniques", + topics: ["Basic joint types", "Travel speed", "Gun angle", "Arc length control"] + } + ], + prerequisites: ["High school diploma or equivalent", "Basic math skills"], + certification: "AWS D1.1 Basic MIG Certification", + instructor: { + name: "Mike Rodriguez", + bio: "Certified Welding Inspector with 15 years of industrial experience", + image: "/images/instructor-mike.jpg" + }, + schedule: [ + { + startDate: new Date('2024-02-05'), + endDate: new Date('2024-02-09'), + location: "Main Training Center", + maxStudents: 12, + enrolledStudents: 8 + } + ], + image: "/images/mig-training.jpg", + featured: true + }, + { + title: "Advanced TIG Welding Mastery", + slug: "advanced-tig-welding", + description: "Master advanced TIG welding techniques for stainless steel and aluminum. Learn precision control and exotic material welding.", + level: "advanced", + duration: "80 hours (2 weeks)", + price: 1899.99, + curriculum: [ + { + module: "Advanced Materials", + topics: ["Stainless steel welding", "Aluminum techniques", "Exotic alloys", "Dissimilar metal joining"] + }, + { + module: "Precision Techniques", + topics: ["Pulse welding", "Walking the cup", "Freehand techniques", "Orbital welding"] + }, + { + module: "Quality Control", + topics: ["Visual inspection", "Penetrant testing", "X-ray interpretation", "Weld defect analysis"] + } + ], + prerequisites: ["Basic TIG welding experience", "AWS D17.1 certification recommended"], + certification: "AWS D17.1 Advanced TIG Certification", + instructor: { + name: "Sarah Chen", + bio: "Master welder specializing in aerospace and nuclear applications", + image: "/images/instructor-sarah.jpg" + }, + schedule: [ + { + startDate: new Date('2024-02-12'), + endDate: new Date('2024-02-23'), + location: "Advanced Training Lab", + maxStudents: 8, + enrolledStudents: 5 + } + ], + image: "/images/tig-training.jpg", + featured: true + }, + { + title: "Welding Inspector Certification", + slug: "welding-inspector-certification", + description: "Comprehensive training for AWS Certified Welding Inspector (CWI) certification. Learn inspection techniques, codes, and standards.", + level: "advanced", + duration: "120 hours (3 weeks)", + price: 2499.99, + curriculum: [ + { + module: "Welding Codes and Standards", + topics: ["AWS D1.1 Structural", "API 1104 Pipeline", "ASME Section IX", "International standards"] + }, + { + module: "Inspection Techniques", + topics: ["Visual inspection", "NDT methods", "Documentation", "Quality assurance"] + }, + { + module: "Metallurgy and Materials", + topics: ["Steel metallurgy", "Heat treatment", "Material properties", "Failure analysis"] + } + ], + prerequisites: ["5+ years welding experience", "High school diploma", "Vision test"], + certification: "AWS Certified Welding Inspector (CWI)", + instructor: { + name: "Robert Thompson", + bio: "Senior CWI with 20+ years in aerospace and nuclear industries", + image: "/images/instructor-robert.jpg" + }, + schedule: [ + { + startDate: new Date('2024-03-01'), + endDate: new Date('2024-03-22'), + location: "Certification Center", + maxStudents: 20, + enrolledStudents: 15 + } + ], + image: "/images/inspector-training.jpg", + featured: false + } + ] + + const createdCourses = await TrainingCourse.insertMany(trainingCourses) + console.log(`Seeded ${createdCourses.length} training courses`) + + // Seed Job Postings + const jobPostings = [ + { + title: "Senior Welding Engineer", + department: "Engineering", + location: "Houston, TX", + type: "full-time", + experience: "5+ years", + salary: { + min: 85000, + max: 120000, + currency: "USD" + }, + description: "Lead welding engineering projects for oil & gas industry clients. Develop welding procedures, oversee quality control, and manage welding operations.", + responsibilities: [ + "Develop and qualify welding procedures (WPS/PQR)", + "Oversee welding operations and quality control", + "Train and mentor junior welding staff", + "Interface with clients on technical requirements", + "Conduct failure analysis and troubleshooting" + ], + requirements: [ + "Bachelor's degree in Welding Engineering or related field", + "AWS Certified Welding Inspector (CWI) required", + "5+ years of industrial welding experience", + "Experience with ASME and API codes", + "Strong leadership and communication skills" + ], + benefits: [ + "Competitive salary with performance bonuses", + "Comprehensive health, dental, and vision insurance", + "401(k) with company matching", + "Professional development opportunities", + "Flexible work arrangements" + ], + skills: ["Welding Engineering", "AWS Codes", "ASME", "API", "Quality Control", "Leadership"], + featured: true, + applicationDeadline: new Date('2024-03-15') + }, + { + title: "Certified Welder - Structural Steel", + department: "Production", + location: "Denver, CO", + type: "full-time", + experience: "3-5 years", + salary: { + min: 55000, + max: 75000, + currency: "USD" + }, + description: "Perform structural steel welding for construction projects. Must be certified in SMAW, GMAW, and FCAW processes.", + responsibilities: [ + "Perform structural steel welding per AWS D1.1", + "Read and interpret welding symbols and blueprints", + "Maintain welding equipment and tools", + "Follow safety procedures and quality standards", + "Complete daily production reports" + ], + requirements: [ + "AWS D1.1 Structural Welding Certification", + "3+ years of structural welding experience", + "Ability to pass welding tests in all positions", + "Basic blueprint reading skills", + "Physical ability to work in various positions" + ], + benefits: [ + "Competitive hourly wages", + "Health insurance", + "Paid time off", + "Safety bonus program", + "Tool allowance" + ], + skills: ["SMAW", "GMAW", "FCAW", "Structural Welding", "Blueprint Reading", "AWS D1.1"], + featured: false, + applicationDeadline: new Date('2024-02-28') + }, + { + title: "Welding Instructor", + department: "Training", + location: "Phoenix, AZ", + type: "full-time", + experience: "5+ years", + salary: { + min: 65000, + max: 85000, + currency: "USD" + }, + description: "Teach welding courses to students of all skill levels. Develop curriculum and maintain training equipment.", + responsibilities: [ + "Conduct welding classes for various skill levels", + "Develop and update training curriculum", + "Maintain training equipment and workshop", + "Assess student progress and provide feedback", + "Prepare students for certification exams" + ], + requirements: [ + "AWS Certified Welding Educator (CWE) preferred", + "5+ years of welding experience", + "Teaching or training experience", + "Excellent communication skills", + "Patience and ability to work with diverse learners" + ], + benefits: [ + "Competitive salary", + "Summer break schedule", + "Professional development funding", + "Health and retirement benefits", + "Job security in growing field" + ], + skills: ["Teaching", "Curriculum Development", "AWS Certification", "Multiple Welding Processes", "Student Assessment"], + featured: true, + applicationDeadline: new Date('2024-04-01') + } + ] + + const createdJobs = await JobPosting.insertMany(jobPostings) + console.log(`Seeded ${createdJobs.length} job postings`) + + console.log('Database seeding completed successfully!') + console.log('\nSeeded data summary:') + console.log(`- ${createdProducts.length} products`) + console.log(`- ${createdIndustries.length} industries`) + console.log(`- ${createdCourses.length} training courses`) + console.log(`- ${createdJobs.length} job postings`) + + } catch (error) { + console.error('Error seeding database:', error) + } finally { + await mongoose.connection.close() + console.log('Database connection closed') + } +} + +seedData() \ No newline at end of file