YusufSyam 3 недель назад
Родитель
Сommit
e072df4920
2 измененных файлов с 359 добавлено и 0 удалено
  1. 2 0
      package.json
  2. 357 0
      src/scripts/seed.ts

+ 2 - 0
package.json

@@ -12,6 +12,7 @@
     "generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types",
     "lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
     "payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
+    "seed": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/seed.ts",
     "start": "cross-env NODE_OPTIONS=--no-deprecation next start -p 3001",
     "test": "pnpm run test:int && pnpm run test:e2e",
     "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test",
@@ -44,6 +45,7 @@
     "playwright": "1.56.1",
     "playwright-core": "1.56.1",
     "prettier": "^3.4.2",
+    "tsx": "^4.19.2",
     "typescript": "5.7.3",
     "vite-tsconfig-paths": "5.1.4",
     "vitest": "3.2.3"

+ 357 - 0
src/scripts/seed.ts

@@ -0,0 +1,357 @@
+import 'dotenv/config'
+import { readFileSync, statSync } from 'fs'
+import { getPayload } from 'payload'
+import path from 'path'
+import { fileURLToPath } from 'url'
+
+import config from '../payload.config'
+
+const filename = fileURLToPath(import.meta.url)
+const dirname = path.dirname(filename)
+
+// Data Careers
+const careers = [
+    {
+        name: 'Code Warrior',
+        imageFile: 'news1.png', // Asumsi file ini ada di folder seed-assets
+        requirements: [
+            'Resourceful, communicative, fast learner, innovative, and good team player',
+            'Hands on experience with Java EE',
+            'Comprehend any Java MVC Frameworks',
+            'Comprehend any Java Persistence Frameworks',
+            'Comprehend any modern RDBMS',
+        ],
+        mainJobDescription: [
+            'Participate in solution design',
+            'Responsible in developing, unit testing, and troubleshooting solution',
+        ],
+        isUrgentlyHiring: true,
+        jobCategory: 'Technology & Engineering',
+    },
+    {
+        name: 'Web Developer',
+        imageFile: 'news2.jpeg',
+        requirements: [
+            'Creative, communicative, fast learner, innovative, and good team player',
+            'Master in modern web technologies',
+            'User experience oriented',
+            'Exposure on mobile development is preferred',
+        ],
+        jobCategory: 'Marketing, Sales & Communication',
+        mainJobDescription: [
+            'Responsible in user experience design',
+            'Responsible in developing, unit testing, and troubleshooting solution',
+        ],
+    },
+    {
+        name: 'Quality Assurance',
+        imageFile: 'news3.jpeg',
+        requirements: [
+            'Thorough, communicative, fast learner, innovative, and good team player',
+            'Hands on experience with testing lifecycle, scenario, and tools',
+            'Comprehend programming logic',
+            'Comprehend any modern RDBMS',
+        ],
+        jobCategory: 'Finance & Accounting',
+        mainJobDescription: [
+            'Responsible in testing lifecycle',
+            'Responsible in solution documentation',
+            'Participate in solution training',
+        ],
+    },
+    {
+        name: 'Tech. Leader',
+        imageFile: 'news4.png',
+        requirements: [
+            'Knowledgeable, proactive, innovative, and good team player',
+            'Minimum 4 years of experience in solution design and development',
+            'Master in any Java MVC and Persistence Frameworks, as well as RDBMS',
+            'Exposure on Service Oriented Architecture/Big Data/Mobile is an advantage',
+        ],
+        jobCategory: 'Creative & Design',
+        mainJobDescription: [
+            'Responsible in technical team management',
+            'Responsible in requirement analysis and solution design',
+            'Responsible in developing, unit testing, deployment, and troubleshooting solution',
+        ],
+    },
+]
+
+// Data Clients
+const clients = [
+    {
+        name: 'Bank Tabungan Negara',
+        href: 'http://www.btn.co.id',
+        imageFile: 'btn.png',
+        imageHeight: 42,
+        category: 'Banking & Finance',
+    },
+    {
+        name: 'Bank Internasional Indonesia',
+        href: 'http://www.bii.co.id',
+        imageFile: 'bii.png',
+        imageHeight: 50,
+        category: 'Banking & Finance',
+    },
+    {
+        name: 'Bank Sinarmas',
+        href: 'http://www.banksinarmas.com',
+        imageFile: 'bsim.png',
+        imageHeight: 42,
+        category: 'Banking & Finance',
+    },
+    {
+        name: 'Telkomsel',
+        href: 'http://telkomsel.com/',
+        imageFile: 'tsel.png',
+        imageHeight: 56,
+        category: 'Enterprise & Industrial',
+    },
+    {
+        name: 'Honda Prospect Motor',
+        href: 'http://www.hpm.co.id',
+        imageFile: 'hpm.jpg',
+        imageHeight: 56,
+        category: 'Enterprise & Industrial',
+    },
+    {
+        name: 'Bank Tabungan Negara - Syariah',
+        href: 'http://www.btn.co.id/Syariah/Home.aspx',
+        imageFile: 'btns1.png',
+        imageHeight: 44,
+        category: 'Banking & Finance',
+    },
+    {
+        name: 'Bank Negara Indonesia',
+        href: 'http://www.bni.co.id',
+        imageFile: 'bni.png',
+        imageHeight: 24,
+        category: 'Banking & Finance',
+    },
+    {
+        name: 'Direktorat Jenderal Bea dan Cukai',
+        href: 'http://www.beacukai.go.id',
+        imageFile: 'djbc.png',
+        imageHeight: 48,
+        category: 'Government',
+    },
+    {
+        name: 'Sinarmas Forestry',
+        href: 'http://www.sinarmasforestry.com/Default.asp',
+        imageFile: 'smf2.png',
+        imageHeight: 48,
+        category: 'Enterprise & Industrial',
+    },
+]
+
+/**
+ * Generate slug from name
+ */
+function generateSlug(name: string): string {
+    return name
+        .toLowerCase()
+        .replace(/[^a-z0-9]+/g, '-')
+        .replace(/^-+|-+$/g, '')
+}
+
+/**
+ * Upload image file to Media collection
+ */
+async function uploadImage(
+    payload: Awaited<ReturnType<typeof getPayload>>,
+    filename: string,
+): Promise<number> {
+    const seedAssetsPath = path.resolve(dirname, '../seed-assets', filename)
+
+    try {
+        // Check if file exists
+        if (!statSync(seedAssetsPath).isFile()) {
+            throw new Error(`File not found: ${seedAssetsPath}`)
+        }
+
+        const fileBuffer = readFileSync(seedAssetsPath)
+        const fileExtension = path.extname(filename).slice(1).toLowerCase()
+
+        // Determine MIME type
+        const mimeTypes: Record<string, string> = {
+            jpg: 'image/jpeg',
+            jpeg: 'image/jpeg',
+            png: 'image/png',
+            gif: 'image/gif',
+            webp: 'image/webp',
+            svg: 'image/svg+xml',
+        }
+        const mimeType = mimeTypes[fileExtension] || 'image/jpeg'
+
+        const altText = filename.replace(/\.[^/.]+$/, '').replace(/[-_]/g, ' ')
+
+        const media = await payload.create({
+            collection: 'media',
+            data: {
+                alt: altText,
+            },
+            file: {
+                data: fileBuffer,
+                mimetype: mimeType,
+                name: filename,
+                size: fileBuffer.length,
+            },
+        })
+
+        // Convert ID to number (Payload uses number IDs for PostgreSQL)
+        return typeof media.id === 'number' ? media.id : Number(media.id)
+    } catch (error) {
+        console.error(`Error uploading ${filename}:`, error)
+        throw error
+    }
+}
+
+/**
+ * Main seeding function
+ */
+async function seed() {
+    console.log('🌱 Starting seed process...\n')
+
+    // Initialize Payload
+    const payloadConfig = await config
+    const payload = await getPayload({ config: payloadConfig })
+
+    try {
+        // Reset collections (optional - uncomment if you want to clear existing data)
+        console.log('🗑️  Clearing existing data...')
+        try {
+            const existingCareers = await payload.find({
+                collection: 'careers',
+                limit: 1000,
+            })
+            for (const career of existingCareers.docs) {
+                await payload.delete({
+                    collection: 'careers',
+                    id: career.id,
+                })
+            }
+            console.log(`   ✓ Deleted ${existingCareers.docs.length} existing careers`)
+        } catch (error) {
+            console.log('   ⚠ No existing careers to delete')
+        }
+
+        try {
+            const existingClients = await payload.find({
+                collection: 'clients',
+                limit: 1000,
+            })
+            for (const client of existingClients.docs) {
+                await payload.delete({
+                    collection: 'clients',
+                    id: client.id,
+                })
+            }
+            console.log(`   ✓ Deleted ${existingClients.docs.length} existing clients`)
+        } catch (error) {
+            console.log('   ⚠ No existing clients to delete')
+        }
+
+        try {
+            const existingMedia = await payload.find({
+                collection: 'media',
+                limit: 1000,
+            })
+            for (const media of existingMedia.docs) {
+                await payload.delete({
+                    collection: 'media',
+                    id: media.id,
+                })
+            }
+            console.log(`   ✓ Deleted ${existingMedia.docs.length} existing media files`)
+        } catch (error) {
+            console.log('   ⚠ No existing media to delete')
+        }
+
+        console.log('\n📤 Seeding Clients...\n')
+
+        // Seed Clients
+        for (const clientData of clients) {
+            try {
+                if (!clientData.imageFile) {
+                    console.log(`   ⚠ Skipping ${clientData.name} - no imageFile specified`)
+                    continue
+                }
+
+                console.log(`   📤 Uploading image for ${clientData.name}...`)
+                const mediaId = await uploadImage(payload, clientData.imageFile)
+
+                console.log(`   ✅ Creating client: ${clientData.name}`)
+                await payload.create({
+                    collection: 'clients',
+                    data: {
+                        name: clientData.name,
+                        href: clientData.href,
+                        logo: mediaId,
+                        category: clientData.category as
+                            | 'Banking & Finance'
+                            | 'Enterprise & Industrial'
+                            | 'Government',
+                        imageHeight: clientData.imageHeight,
+                    },
+                })
+
+                console.log(`   ✓ Success: ${clientData.name}\n`)
+            } catch (error) {
+                console.error(`   ✗ Error seeding client ${clientData.name}:`, error)
+            }
+        }
+
+        console.log('📤 Seeding Careers...\n')
+
+        // Seed Careers
+        for (const careerData of careers) {
+            try {
+                let mediaId: number | undefined
+
+                if (careerData.imageFile) {
+                    console.log(`   📤 Uploading image for ${careerData.name}...`)
+                    mediaId = await uploadImage(payload, careerData.imageFile)
+                }
+
+                const slug = generateSlug(careerData.name)
+
+                console.log(`   ✅ Creating career: ${careerData.name}`)
+                await payload.create({
+                    collection: 'careers',
+                    data: {
+                        title: careerData.name,
+                        slug: slug,
+                        image: mediaId ?? null,
+                        requirements: careerData.requirements.map((req) => ({ item: req })),
+                        mainJobDescription: careerData.mainJobDescription.map((desc) => ({
+                            item: desc,
+                        })),
+                        isUrgentlyHiring: careerData.isUrgentlyHiring ?? false,
+                        jobCategory: careerData.jobCategory as
+                            | 'Technology & Engineering'
+                            | 'Marketing, Sales & Communication'
+                            | 'Finance & Accounting'
+                            | 'Human Resources & General Affairs'
+                            | 'Creative & Design'
+                            | 'Operations & Customer Success',
+                    },
+                })
+
+                console.log(`   ✓ Success: ${careerData.name}\n`)
+            } catch (error) {
+                console.error(`   ✗ Error seeding career ${careerData.name}:`, error)
+            }
+        }
+
+        console.log('✨ Seed process completed successfully!')
+    } catch (error) {
+        console.error('❌ Seed process failed:', error)
+        process.exit(1)
+    }
+
+    process.exit(0)
+}
+
+// Run seed
+seed()
+