168 lines
4.9 KiB
TypeScript
168 lines
4.9 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|