import { NextRequest, NextResponse } from 'next/server'; import * as XLSX from 'xlsx'; import supabase from '@/lib/db'; // POST - Upload dan proses file Excel/CSV untuk data dosen export async function POST(request: NextRequest) { try { // Ambil data formulir dari request const formData = await request.formData(); const file = formData.get('file') as File; if (!file) { return NextResponse.json( { message: 'File is required' }, { status: 400 } ); } // Validasi tipe file (Excel atau CSV) const allowedTypes = [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx 'application/vnd.ms-excel', // .xls 'text/csv' // .csv ]; if (!allowedTypes.includes(file.type)) { return NextResponse.json( { message: 'File type not supported. Please upload Excel (.xlsx, .xls) or CSV file.' }, { status: 400 } ); } // Baca file sebagai buffer const buffer = Buffer.from(await file.arrayBuffer()); // Parse file menggunakan XLSX const workbook = XLSX.read(buffer, { type: 'buffer' }); const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; // Konversi ke JSON const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); if (jsonData.length === 0) { return NextResponse.json( { message: 'File is empty' }, { status: 400 } ); } // Ambil header dari baris pertama const headers = jsonData[0] as string[]; const dataRows = jsonData.slice(1); // Validasi header yang diperlukan const requiredHeaders = ['nama_dosen', 'nip']; const missingHeaders = requiredHeaders.filter(header => !headers.some(h => h && h.toString().toLowerCase().includes(header.toLowerCase())) ); if (missingHeaders.length > 0) { return NextResponse.json( { message: `Missing required columns: ${missingHeaders.join(', ')}` }, { status: 400 } ); } // Mapping index kolom const getColumnIndex = (headerName: string) => { return headers.findIndex(h => h && h.toString().toLowerCase().includes(headerName.toLowerCase())); }; const nama_dosenIndex = getColumnIndex('nama_dosen'); const nipIndex = getColumnIndex('nip'); // Proses setiap baris data const results = { success: 0, failed: 0, errors: [] as string[] }; for (let i = 0; i < dataRows.length; i++) { const row = dataRows[i] as any[]; const rowNumber = i + 2; // +2 karena index dimulai dari 0 dan ada header try { // Ekstrak data dari row const nama_dosen = row[nama_dosenIndex]?.toString().trim(); const nip = row[nipIndex]?.toString().trim(); // Validasi data yang diperlukan if (!nama_dosen || !nip) { results.failed++; results.errors.push(`Row ${rowNumber}: Missing required data (nama_dosen or nip)`); continue; } // Validasi panjang NIP if (nip.length !== 18) { results.failed++; results.errors.push(`Row ${rowNumber}: NIP harus terdiri dari 18 karakter`); continue; } // Cek apakah NIP sudah ada const { data: existingDosen, error: checkError } = await supabase .from('dosen') .select('nip') .eq('nip', nip) .single(); if (!checkError && existingDosen) { // Update data yang sudah ada const { error: updateError } = await supabase .from('dosen') .update({ nama_dosen }) .eq('nip', nip); if (updateError) { results.failed++; results.errors.push(`Row ${rowNumber}: Error updating dosen with NIP ${nip}: ${updateError.message}`); continue; } } else { // Insert data baru const { error: insertError } = await supabase .from('dosen') .insert({ nama_dosen, nip }); if (insertError) { results.failed++; results.errors.push(`Row ${rowNumber}: Error inserting dosen with NIP ${nip}: ${insertError.message}`); continue; } } results.success++; } catch (error) { results.failed++; results.errors.push(`Row ${rowNumber}: Unexpected error: ${error}`); } } return NextResponse.json({ message: `Upload completed. Success: ${results.success}, Failed: ${results.failed}`, details: { totalRows: dataRows.length, successCount: results.success, failedCount: results.failed, errors: results.errors } }); } catch (error) { console.error('Error processing file:', error); return NextResponse.json( { message: 'Error processing file' }, { status: 500 } ); } }