Deploy Laravel rebuild via Coolify

Point Docker and Coolify compose to the Laravel rebuild app so mahasiswa, dosen, and admin flows are served from the new Laravel public entrypoint.
This commit is contained in:
Power BI Dev
2026-05-03 18:50:29 +07:00
parent 89ce9d30a7
commit dab8ea396b
107 changed files with 17544 additions and 20 deletions

View File

@@ -0,0 +1,801 @@
<?php
namespace App\Http\Controllers;
use App\Support\AdminNavigation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class AdminPageController extends Controller
{
public function mahasiswa(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tbmhs as tm')
->leftJoin('tbprodi as tp', 'tm.idProdi', '=', 'tp.idProdi')
->select(['tm.idmhs', 'tm.nim', 'tm.nmLengkap', 'tp.nmProdi', 'tm.thnmasuk', 'tm.status'])
->where('tm.idProdi', $user['prodi'])
->orderByDesc('tm.idmhs')
->paginate(20);
return $this->table($request, 'Data Mahasiswa', 'Data mahasiswa pada program studi admin aktif.', ['NIM', 'Nama', 'Prodi', 'Angkatan', 'Status'], $rows, fn ($row) => [$row->nim, $row->nmLengkap, $row->nmProdi, $row->thnmasuk ?: '-', $row->status], 'admin.data.mahasiswa', [
'actions' => [
['label' => 'Tambah Mahasiswa', 'href' => route('admin.data.mahasiswa.create', [], false), 'variant' => 'dark'],
['label' => 'Impor CSV Mahasiswa', 'href' => route('admin.data.mahasiswa.import', [], false), 'variant' => 'dark'],
],
'rowActions' => fn ($row) => [
['label' => 'Edit', 'href' => route('admin.data.mahasiswa.edit', ['id' => $row->idmhs], false)],
['label' => 'Hapus', 'href' => route('admin.data.mahasiswa.destroy', ['id' => $row->idmhs], false), 'method' => 'DELETE', 'confirm' => 'Hapus mahasiswa ini?'],
],
]);
}
public function createMahasiswa(Request $request): View
{
return $this->resourceForm($request, 'Tambah Mahasiswa', 'Tambah data mahasiswa pada program studi admin.', route('admin.data.mahasiswa.store'), route('admin.data.mahasiswa', [], false), $this->mahasiswaFields());
}
public function storeMahasiswa(Request $request): RedirectResponse
{
$user = $this->user($request);
$data = $request->validate([
'nim' => ['required', 'string', 'max:30'],
'nmLengkap' => ['required', 'string', 'max:255'],
'email' => ['nullable', 'email', 'max:255'],
'thnmasuk' => ['nullable', 'string', 'max:10'],
'password' => ['required', 'string', 'min:4'],
'status' => ['required', 'in:A,N,P'],
'bolehUploadDraft' => ['required', 'in:0,1'],
'noHP' => ['nullable', 'string', 'max:30'],
'noHPOrtu' => ['nullable', 'string', 'max:30'],
]);
$data['idProdi'] = $user['prodi'];
$data['password'] = md5($data['password']);
DB::table('tbmhs')->insert($data);
return redirect()->route('admin.data.mahasiswa')->with('success', 'Data mahasiswa berhasil ditambahkan.');
}
public function editMahasiswa(Request $request, int $id): View
{
$user = $this->user($request);
$row = DB::table('tbmhs')->where('idmhs', $id)->where('idProdi', $user['prodi'])->first();
abort_unless($row, 404);
return $this->resourceForm($request, 'Edit Mahasiswa', 'Perbarui data mahasiswa.', route('admin.data.mahasiswa.update', ['id' => $id]), route('admin.data.mahasiswa', [], false), $this->mahasiswaFields($row, true), 'PUT');
}
public function updateMahasiswa(Request $request, int $id): RedirectResponse
{
$user = $this->user($request);
abort_unless(DB::table('tbmhs')->where('idmhs', $id)->where('idProdi', $user['prodi'])->exists(), 404);
$data = $request->validate([
'nim' => ['required', 'string', 'max:30'],
'nmLengkap' => ['required', 'string', 'max:255'],
'email' => ['nullable', 'email', 'max:255'],
'thnmasuk' => ['nullable', 'string', 'max:10'],
'password' => ['nullable', 'string', 'min:4'],
'status' => ['required', 'in:A,N,P'],
'bolehUploadDraft' => ['required', 'in:0,1'],
'noHP' => ['nullable', 'string', 'max:30'],
'noHPOrtu' => ['nullable', 'string', 'max:30'],
]);
if (($data['password'] ?? '') !== '') {
$data['password'] = md5($data['password']);
} else {
unset($data['password']);
}
DB::table('tbmhs')->where('idmhs', $id)->update($data);
return redirect()->route('admin.data.mahasiswa')->with('success', 'Data mahasiswa berhasil diperbarui.');
}
public function destroyMahasiswa(Request $request, int $id): RedirectResponse
{
$user = $this->user($request);
DB::table('tbmhs')->where('idmhs', $id)->where('idProdi', $user['prodi'])->delete();
return redirect()->route('admin.data.mahasiswa')->with('success', 'Data mahasiswa berhasil dihapus.');
}
public function importMahasiswa(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
return view('admin.pages.mahasiswa-import', [
'title' => 'Impor CSV Mahasiswa | SPOTA Rebuild',
'pageTitle' => 'Impor CSV Mahasiswa',
'pageDescription' => 'Upload CSV untuk menambah atau memperbarui data mahasiswa secara bulk berdasarkan NIM.',
'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'),
'sidebar' => AdminNavigation::build($user),
'user' => $user,
'requiredHeaders' => ['nim', 'nama', 'email', 'thnmasuk'],
'optionalHeaders' => ['password', 'status', 'noHP', 'noHPOrtu', 'bolehUploadDraft'],
]);
}
public function storeImportMahasiswa(Request $request): RedirectResponse
{
$user = $this->user($request);
$this->abortIfSuper($user);
$validated = $request->validate([
'csv' => ['required', 'file', 'mimes:csv,txt', 'max:5120'],
'mode' => ['required', 'in:insert_only,upsert'],
'default_password' => ['nullable', 'string', 'min:4', 'max:50'],
]);
$path = $request->file('csv')->getRealPath();
$handle = fopen($path, 'r');
if (! $handle) {
return back()->with('error', 'File CSV tidak dapat dibaca.');
}
$headers = fgetcsv($handle);
if (! $headers) {
fclose($handle);
return back()->with('error', 'CSV kosong atau header tidak ditemukan.');
}
$headers = array_map(fn ($header) => trim((string) $header), $headers);
$normalized = array_map(fn ($header) => strtolower($header), $headers);
$aliases = [
'nama_lengkap' => 'nama',
'nmlengkap' => 'nama',
'tahunmasuk' => 'thnmasuk',
'tahun_masuk' => 'thnmasuk',
'nohp' => 'noHP',
'no_hp' => 'noHP',
'nohportu' => 'noHPOrtu',
'no_hp_ortu' => 'noHPOrtu',
'bolehuploaddraft' => 'bolehUploadDraft',
'boleh_upload_draft' => 'bolehUploadDraft',
];
$canonical = [];
foreach ($normalized as $index => $header) {
$canonical[$index] = $aliases[$header] ?? $headers[$index];
}
$required = ['nim', 'nama', 'email', 'thnmasuk'];
$missing = array_diff($required, $canonical);
if (! empty($missing)) {
fclose($handle);
return back()->with('error', 'Header CSV wajib belum lengkap: '.implode(', ', $missing).'.');
}
$inserted = 0;
$updated = 0;
$skipped = 0;
$errors = [];
$line = 1;
$defaultPassword = $validated['default_password'] ?: '123456';
DB::beginTransaction();
try {
while (($row = fgetcsv($handle)) !== false) {
$line++;
$data = [];
foreach ($canonical as $index => $header) {
$data[$header] = trim((string) ($row[$index] ?? ''));
}
if (count(array_filter($data, fn ($value) => $value !== '')) === 0) {
continue;
}
$nim = $data['nim'] ?? '';
$nama = $data['nama'] ?? '';
$email = $data['email'] ?? '';
$thnmasuk = $data['thnmasuk'] ?? '';
if ($nim === '' || $nama === '' || $email === '' || $thnmasuk === '') {
$skipped++;
$errors[] = 'Baris '.$line.': nim, nama, email, dan thnmasuk wajib diisi.';
continue;
}
if (! preg_match('/^[A-Za-z0-9._-]+$/', $nim)) {
$skipped++;
$errors[] = 'Baris '.$line.': NIM tidak valid.';
continue;
}
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
$skipped++;
$errors[] = 'Baris '.$line.': email tidak valid.';
continue;
}
$payload = [
'nim' => $nim,
'nmLengkap' => $nama,
'email' => $email,
'idProdi' => $user['prodi'],
'thnmasuk' => $thnmasuk,
'status' => ($data['status'] ?? '') !== '' ? $data['status'] : 'A',
'noHP' => $data['noHP'] ?? '',
'noHPOrtu' => $data['noHPOrtu'] ?? '',
'bolehUploadDraft' => ($data['bolehUploadDraft'] ?? '') !== '' ? $data['bolehUploadDraft'] : '1',
];
$existing = DB::table('tbmhs')
->where('nim', $nim)
->where('idProdi', $user['prodi'])
->first();
if ($existing && $validated['mode'] === 'insert_only') {
$skipped++;
continue;
}
if ($existing) {
if (($data['password'] ?? '') !== '') {
$payload['password'] = md5($data['password']);
}
DB::table('tbmhs')->where('idmhs', $existing->idmhs)->update($payload);
$updated++;
} else {
$payload['password'] = md5(($data['password'] ?? '') !== '' ? $data['password'] : $defaultPassword);
DB::table('tbmhs')->insert($payload);
$inserted++;
}
}
DB::commit();
} catch (\Throwable $exception) {
DB::rollBack();
fclose($handle);
return back()->with('error', 'Import gagal: '.$exception->getMessage());
}
fclose($handle);
return redirect()
->route('admin.data.mahasiswa')
->with('success', 'Import selesai. Baru: '.$inserted.', diperbarui: '.$updated.', dilewati: '.$skipped.'.'.($errors ? ' Catatan: '.implode(' ', array_slice($errors, 0, 5)) : ''));
}
public function dosen(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tbdosen as td')
->leftJoin('tbprodi as tp', 'td.idProdi', '=', 'tp.idProdi')
->select(['td.iddosen', 'td.nip', 'td.nmLengkap', 'td.email', 'tp.nmProdi', 'td.status'])
->where('td.idProdi', $user['prodi'])
->orderBy('td.nmLengkap')
->paginate(20);
return $this->table($request, 'Data Dosen', 'Data dosen pada program studi admin aktif.', ['NIP', 'Nama', 'Email', 'Prodi', 'Status'], $rows, fn ($row) => [$row->nip, $row->nmLengkap, $row->email ?: '-', $row->nmProdi, $row->status], 'admin.data.dosen', [
'actions' => [
['label' => 'Tambah Dosen', 'href' => route('admin.data.dosen.create', [], false), 'variant' => 'dark'],
],
'rowActions' => fn ($row) => [
['label' => 'Edit', 'href' => route('admin.data.dosen.edit', ['id' => $row->iddosen], false)],
['label' => 'Hapus', 'href' => route('admin.data.dosen.destroy', ['id' => $row->iddosen], false), 'method' => 'DELETE', 'confirm' => 'Hapus dosen ini?'],
],
]);
}
public function createDosen(Request $request): View
{
return $this->resourceForm($request, 'Tambah Dosen', 'Tambah data dosen pada program studi admin.', route('admin.data.dosen.store'), route('admin.data.dosen', [], false), $this->dosenFields());
}
public function storeDosen(Request $request): RedirectResponse
{
$user = $this->user($request);
$data = $request->validate([
'nip' => ['required', 'string', 'max:30'],
'nmLengkap' => ['required', 'string', 'max:255'],
'email' => ['nullable', 'email', 'max:255'],
'nohp' => ['nullable', 'string', 'max:30'],
'jenis' => ['required', 'string', 'max:10'],
'password' => ['required', 'string', 'min:4'],
'status' => ['required', 'in:A,N,P'],
]);
$data['idProdi'] = $user['prodi'];
$data['password'] = md5($data['password']);
DB::table('tbdosen')->insert($data);
return redirect()->route('admin.data.dosen')->with('success', 'Data dosen berhasil ditambahkan.');
}
public function editDosen(Request $request, int $id): View
{
$user = $this->user($request);
$row = DB::table('tbdosen')->where('iddosen', $id)->where('idProdi', $user['prodi'])->first();
abort_unless($row, 404);
return $this->resourceForm($request, 'Edit Dosen', 'Perbarui data dosen.', route('admin.data.dosen.update', ['id' => $id]), route('admin.data.dosen', [], false), $this->dosenFields($row, true), 'PUT');
}
public function updateDosen(Request $request, int $id): RedirectResponse
{
$user = $this->user($request);
abort_unless(DB::table('tbdosen')->where('iddosen', $id)->where('idProdi', $user['prodi'])->exists(), 404);
$data = $request->validate([
'nip' => ['required', 'string', 'max:30'],
'nmLengkap' => ['required', 'string', 'max:255'],
'email' => ['nullable', 'email', 'max:255'],
'nohp' => ['nullable', 'string', 'max:30'],
'jenis' => ['required', 'string', 'max:10'],
'password' => ['nullable', 'string', 'min:4'],
'status' => ['required', 'in:A,N,P'],
]);
if (($data['password'] ?? '') !== '') {
$data['password'] = md5($data['password']);
} else {
unset($data['password']);
}
DB::table('tbdosen')->where('iddosen', $id)->update($data);
return redirect()->route('admin.data.dosen')->with('success', 'Data dosen berhasil diperbarui.');
}
public function destroyDosen(Request $request, int $id): RedirectResponse
{
$user = $this->user($request);
DB::table('tbdosen')->where('iddosen', $id)->where('idProdi', $user['prodi'])->delete();
return redirect()->route('admin.data.dosen')->with('success', 'Data dosen berhasil dihapus.');
}
public function kk(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tb_kelompok_keahlian as kk')
->leftJoin('tbdosen as ketua', 'kk.ketuaKK', '=', 'ketua.iddosen')
->leftJoin('tbdosen as sekretaris', 'kk.sekretarisKK', '=', 'sekretaris.iddosen')
->select(['kk.idKK', 'kk.namaKK', 'kk.warnaLabel', 'ketua.nmLengkap as ketua', 'sekretaris.nmLengkap as sekretaris'])
->orderBy('kk.namaKK')
->paginate(20);
return $this->table($request, 'Data Kelompok Keahlian', 'Daftar kelompok keahlian yang dipakai pada praoutline dan penawaran judul.', ['ID', 'Nama KK', 'Label', 'Ketua', 'Sekretaris'], $rows, fn ($row) => [$row->idKK, $row->namaKK, $row->warnaLabel ?: '-', $row->ketua ?: '-', $row->sekretaris ?: '-'], 'admin.data.kk', [
'actions' => [
['label' => 'Tambah KK', 'href' => route('admin.data.kk.create', [], false), 'variant' => 'dark'],
],
'rowActions' => fn ($row) => [
['label' => 'Edit', 'href' => route('admin.data.kk.edit', ['id' => $row->idKK], false)],
['label' => 'Hapus', 'href' => route('admin.data.kk.destroy', ['id' => $row->idKK], false), 'method' => 'DELETE', 'confirm' => 'Hapus kelompok keahlian ini?'],
],
]);
}
public function createKk(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
return $this->resourceForm($request, 'Tambah Kelompok Keahlian', 'Tambah data kelompok keahlian.', route('admin.data.kk.store'), route('admin.data.kk', [], false), $this->kkFields());
}
public function storeKk(Request $request): RedirectResponse
{
$user = $this->user($request);
$this->abortIfSuper($user);
$data = $request->validate([
'namaKK' => ['required', 'string', 'max:255'],
'warnaLabel' => ['nullable', 'string', 'max:50'],
'ketuaKK' => ['nullable', 'integer'],
'sekretarisKK' => ['nullable', 'integer'],
]);
DB::table('tb_kelompok_keahlian')->insert($data);
return redirect()->route('admin.data.kk')->with('success', 'Data KK berhasil ditambahkan.');
}
public function editKk(Request $request, int $id): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$row = DB::table('tb_kelompok_keahlian')->where('idKK', $id)->first();
abort_unless($row, 404);
return $this->resourceForm($request, 'Edit Kelompok Keahlian', 'Perbarui data kelompok keahlian.', route('admin.data.kk.update', ['id' => $id]), route('admin.data.kk', [], false), $this->kkFields($row), 'PUT');
}
public function updateKk(Request $request, int $id): RedirectResponse
{
$user = $this->user($request);
$this->abortIfSuper($user);
$data = $request->validate([
'namaKK' => ['required', 'string', 'max:255'],
'warnaLabel' => ['nullable', 'string', 'max:50'],
'ketuaKK' => ['nullable', 'integer'],
'sekretarisKK' => ['nullable', 'integer'],
]);
DB::table('tb_kelompok_keahlian')->where('idKK', $id)->update($data);
return redirect()->route('admin.data.kk')->with('success', 'Data KK berhasil diperbarui.');
}
public function destroyKk(Request $request, int $id): RedirectResponse
{
$user = $this->user($request);
$this->abortIfSuper($user);
DB::table('tb_kelompok_keahlian')->where('idKK', $id)->delete();
return redirect()->route('admin.data.kk')->with('success', 'Data KK berhasil dihapus.');
}
public function fakultas(Request $request): View
{
$user = $this->user($request);
$this->abortUnlessSuper($user);
$rows = DB::table('tbfakultas')->select(['idFak', 'nmFakultas'])->orderBy('nmFakultas')->paginate(20);
return $this->table($request, 'Data Fakultas', 'Data fakultas untuk superadmin.', ['ID', 'Nama Fakultas'], $rows, fn ($row) => [$row->idFak, $row->nmFakultas], 'admin.data.fakultas');
}
public function jurusan(Request $request): View
{
$user = $this->user($request);
$this->abortUnlessSuper($user);
$rows = DB::table('tbjurusan as tj')
->leftJoin('tbfakultas as tf', 'tj.idFak', '=', 'tf.idFak')
->select(['tj.idJur', 'tj.nmJurusan', 'tf.nmFakultas'])
->orderBy('tj.nmJurusan')
->paginate(20);
return $this->table($request, 'Data Jurusan', 'Data jurusan untuk superadmin.', ['ID', 'Jurusan', 'Fakultas'], $rows, fn ($row) => [$row->idJur, $row->nmJurusan, $row->nmFakultas ?: '-'], 'admin.data.jurusan');
}
public function prodi(Request $request): View
{
$user = $this->user($request);
$this->abortUnlessSuper($user);
$rows = DB::table('tbprodi as tp')
->leftJoin('tbjurusan as tj', 'tp.idJur', '=', 'tj.idJur')
->leftJoin('tbfakultas as tf', 'tp.idFak', '=', 'tf.idFak')
->select(['tp.idProdi', 'tp.nmProdi', 'tj.nmJurusan', 'tf.nmFakultas'])
->orderBy('tp.nmProdi')
->paginate(20);
return $this->table($request, 'Data Program Studi', 'Data program studi untuk superadmin.', ['ID', 'Program Studi', 'Jurusan', 'Fakultas'], $rows, fn ($row) => [$row->idProdi, $row->nmProdi, $row->nmJurusan ?: '-', $row->nmFakultas ?: '-'], 'admin.data.prodi');
}
public function praoutline(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = $this->praoutlineBase($user)
->orderByDesc('tp.id')
->paginate(20);
return $this->table($request, 'Daftar Draft Praoutline', 'Draft praoutline terbaru pada program studi admin.', ['ID', 'Mahasiswa', 'Judul', 'Tanggal Upload', 'Status'], $rows, fn ($row) => [$row->id, $row->mahasiswa.' ('.$row->nim.')', $row->judul, $this->formatDateTime(trim($row->tgl_upload.' '.$row->wkt_upload)), $this->statusLabel($row->status_usulan)], 'admin.praoutline.index');
}
public function praoutlineSearch(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$keyword = trim((string) $request->query('q'));
$query = $this->praoutlineBase($user)->orderByDesc('tp.id');
if ($keyword !== '') {
$query->where(function ($query) use ($keyword) {
$query->where('tp.judul', 'like', '%'.$keyword.'%')
->orWhere('tm.nmLengkap', 'like', '%'.$keyword.'%')
->orWhere('tp.nim', 'like', '%'.$keyword.'%');
});
}
$rows = $query->paginate(20)->withQueryString();
return $this->table($request, 'Pencarian Praoutline', 'Cari draft berdasarkan judul, nama mahasiswa, atau NIM.', ['ID', 'Mahasiswa', 'Judul', 'Tanggal Upload', 'Status'], $rows, fn ($row) => [$row->id, $row->mahasiswa.' ('.$row->nim.')', $row->judul, $this->formatDateTime(trim($row->tgl_upload.' '.$row->wkt_upload)), $this->statusLabel($row->status_usulan)], 'admin.praoutline.search', ['search' => true, 'keyword' => $keyword]);
}
public function keputusan(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tbrekaphasil as trh')
->leftJoin('tbmhs as tm', 'trh.nim', '=', 'tm.nim')
->leftJoin('tbpraoutline as tp', 'trh.idpraoutline', '=', 'tp.id')
->select(['trh.id', 'trh.nim', 'tm.nmLengkap as mahasiswa', 'tp.judul', 'trh.kep_akhir', 'trh.tgl'])
->where('trh.idProdi', $user['prodi'])
->orderByDesc('trh.id')
->paginate(20);
return $this->table($request, 'Kep. Penunjukan Dosen', 'Rekap keputusan dan penunjukan dosen dari data praoutline.', ['ID', 'Mahasiswa', 'Judul', 'Keputusan', 'Tanggal'], $rows, fn ($row) => [$row->id, ($row->mahasiswa ?: '-').' ('.$row->nim.')', $row->judul ?: '-', $this->statusLabel($row->kep_akhir), $this->formatDateTime($row->tgl)], 'admin.praoutline.keputusan');
}
public function kepDraft(Request $request): View
{
return $this->keputusan($request)->with('pageTitle', 'Kep. Draft Praoutline');
}
public function pemberitahuan(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tmp_notif_r as tn')
->select(['tn.id', 'tn.msg', 'tn.tgl', 'tn.jns_usr', 'tn.idkonten', 'tn.read'])
->where('tn.idProdi', $user['prodi'])
->orderByDesc('tn.id')
->paginate(20);
return $this->table($request, 'Pemberitahuan', 'Pemberitahuan praoutline yang tercatat pada sistem.', ['ID', 'Isi', 'Jenis User', 'Konten', 'Status', 'Tanggal'], $rows, fn ($row) => [$row->id, $row->msg, $row->jns_usr, $row->idkonten, $row->read === 'Y' ? 'Dibaca' : 'Belum Dibaca', $this->formatDateTime($row->tgl)], 'admin.praoutline.pemberitahuan');
}
public function pengumuman(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tbpengumuman')
->select(['id', 'judul', 'tujuan', 'publish', 'tgl'])
->where('idProdi', $user['prodi'])
->orderByDesc('id')
->paginate(20);
return $this->table($request, 'Daftar Pengumuman', 'Pengumuman program studi yang ditampilkan di SPOTA.', ['ID', 'Judul', 'Tujuan', 'Publish', 'Tanggal'], $rows, fn ($row) => [$row->id, $row->judul, $row->tujuan, $row->publish, $this->formatDateTime($row->tgl)], 'admin.pengumuman.index', ['actions' => [['label' => 'Buat Pengumuman Baru', 'href' => route('admin.pengumuman.create', [], false), 'variant' => 'dark']]]);
}
public function createPengumuman(Request $request): View
{
return $this->form($request, 'Buat Pengumuman Baru', 'Tambah pengumuman untuk mahasiswa, dosen, atau seluruh pengguna program studi.', 'admin.pengumuman.create');
}
public function storePengumuman(Request $request): RedirectResponse
{
$user = $this->user($request);
$this->abortIfSuper($user);
$validated = $request->validate([
'judul' => ['required', 'string', 'max:255'],
'isi' => ['required', 'string'],
'tujuan' => ['required', 'in:A,M,D'],
'publish' => ['nullable', 'in:Y,N'],
]);
DB::table('tbpengumuman')->insert([
'idProdi' => $user['prodi'],
'judul' => $validated['judul'],
'isi' => $validated['isi'],
'tujuan' => $validated['tujuan'],
'tgl' => Carbon::now()->toDateTimeString(),
'author' => $user['id'],
'publish' => $validated['publish'] ?? 'Y',
]);
return redirect()->route('admin.pengumuman.index')->with('success', 'Pengumuman berhasil dibuat.');
}
public function jadwal(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('tbjadwal as tj')
->leftJoin('tbmhs as tm', 'tj.idMhs', '=', 'tm.idmhs')
->select(['tj.id', 'tj.jenis', 'tj.start', 'tj.ruangan', 'tj.publish', 'tm.nmLengkap as mahasiswa'])
->where('tj.idProdi', $user['prodi'])
->orderByDesc('tj.start')
->paginate(20);
return $this->table($request, 'Manajemen Data Jadwal', 'Data jadwal seminar/sidang program studi.', ['ID', 'Jenis', 'Mahasiswa', 'Tanggal', 'Ruangan', 'Publish'], $rows, fn ($row) => [$row->id, $row->jenis, $row->mahasiswa ?: '-', $this->formatDateTime($row->start), $row->ruangan ?: '-', $row->publish], 'admin.jadwal.index');
}
public function kalender(Request $request): View
{
return $this->jadwal($request)->with('pageTitle', 'Kalender Seminar/Sidang');
}
public function profile(Request $request): View
{
$user = $this->user($request);
$rows = collect([(object) [
'username' => $user['username'],
'nama' => $user['nama_lengkap'],
'jabatan' => $user['jabatan'] ?: '-',
'email' => $user['email'] ?: '-',
'prodi' => $user['nmprodi'],
'level' => $user['lvl'] === 'S' ? 'Super Admin' : 'Admin Prodi',
]]);
return $this->staticTable($request, 'Profil Saya', 'Profil admin yang sedang login.', ['Username', 'Nama', 'Jabatan', 'Email', 'Prodi', 'Level'], $rows, fn ($row) => [$row->username, $row->nama, $row->jabatan, $row->email, $row->prodi, $row->level], 'admin.profile');
}
public function users(Request $request): View
{
$user = $this->user($request);
$this->abortUnlessSuper($user);
$rows = DB::table('tbadmin as ta')
->leftJoin('tbprodi as tp', 'ta.idProdi', '=', 'tp.idProdi')
->select(['ta.username', 'ta.nmLengkap', 'ta.jenisAdmin', 'ta.aktif', 'tp.nmProdi'])
->orderBy('ta.username')
->paginate(20);
return $this->table($request, 'Manajemen Admin', 'Daftar akun administrator SPOTA.', ['Username', 'Nama', 'Level', 'Prodi', 'Aktif'], $rows, fn ($row) => [$row->username, $row->nmLengkap, $row->jenisAdmin, $row->nmProdi ?: 'Semua', $row->aktif], 'admin.users');
}
public function pengaturan(Request $request): View
{
$user = $this->user($request);
$this->abortIfSuper($user);
$rows = DB::table('web_setting')
->select(['name', 'values'])
->where('idProdi', $user['prodi'])
->orderBy('name')
->paginate(20);
return $this->table($request, 'Pengaturan Prodi', 'Pengaturan aktif program studi pada SPOTA.', ['Nama', 'Nilai'], $rows, fn ($row) => [$row->name, $row->values], 'admin.pengaturan');
}
private function table(Request $request, string $title, string $description, array $columns, mixed $rows, callable $map, string $activeRoute, array $extra = []): View
{
$user = $this->user($request);
return view('admin.pages.table', array_merge([
'title' => $title.' | SPOTA Rebuild',
'pageTitle' => $title,
'pageDescription' => $description,
'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'),
'sidebar' => AdminNavigation::build($user),
'user' => $user,
'columns' => $columns,
'rows' => $rows,
'map' => $map,
], $extra));
}
private function staticTable(Request $request, string $title, string $description, array $columns, mixed $rows, callable $map, string $activeRoute): View
{
return $this->table($request, $title, $description, $columns, $rows, $map, $activeRoute);
}
private function form(Request $request, string $title, string $description, string $activeRoute): View
{
$user = $this->user($request);
return view('admin.pages.pengumuman-form', [
'title' => $title.' | SPOTA Rebuild',
'pageTitle' => $title,
'pageDescription' => $description,
'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'),
'sidebar' => AdminNavigation::build($user),
'user' => $user,
]);
}
private function resourceForm(Request $request, string $title, string $description, string $action, string $cancel, array $fields, string $method = 'POST'): View
{
$user = $this->user($request);
return view('admin.pages.resource-form', [
'title' => $title.' | SPOTA Rebuild',
'pageTitle' => $title,
'pageDescription' => $description,
'sidebar' => AdminNavigation::build($user),
'user' => $user,
'action' => $action,
'cancel' => $cancel,
'fields' => $fields,
'method' => $method,
]);
}
private function mahasiswaFields(?object $row = null, bool $editing = false): array
{
return [
['name' => 'nim', 'label' => 'NIM', 'value' => $row->nim ?? '', 'required' => true],
['name' => 'nmLengkap', 'label' => 'Nama Lengkap', 'value' => $row->nmLengkap ?? '', 'required' => true],
['name' => 'email', 'label' => 'Email', 'type' => 'email', 'value' => $row->email ?? ''],
['name' => 'thnmasuk', 'label' => 'Tahun Masuk', 'value' => $row->thnmasuk ?? ''],
['name' => 'password', 'label' => 'Password', 'type' => 'password', 'required' => ! $editing, 'help' => $editing ? 'Kosongkan jika tidak mengganti password.' : 'Password awal mahasiswa.'],
['name' => 'status', 'label' => 'Status', 'type' => 'select', 'value' => $row->status ?? 'A', 'options' => ['A' => 'Aktif', 'P' => 'Pending', 'N' => 'Nonaktif']],
['name' => 'bolehUploadDraft', 'label' => 'Boleh Upload Draft', 'type' => 'select', 'value' => $row->bolehUploadDraft ?? '1', 'options' => ['1' => 'Ya', '0' => 'Tidak']],
['name' => 'noHP', 'label' => 'No HP', 'value' => $row->noHP ?? ''],
['name' => 'noHPOrtu', 'label' => 'No HP Orang Tua', 'value' => $row->noHPOrtu ?? ''],
];
}
private function dosenFields(?object $row = null, bool $editing = false): array
{
return [
['name' => 'nip', 'label' => 'NIP', 'value' => $row->nip ?? '', 'required' => true],
['name' => 'nmLengkap', 'label' => 'Nama Lengkap', 'value' => $row->nmLengkap ?? '', 'required' => true],
['name' => 'email', 'label' => 'Email', 'type' => 'email', 'value' => $row->email ?? ''],
['name' => 'nohp', 'label' => 'No HP', 'value' => $row->nohp ?? ''],
['name' => 'jenis', 'label' => 'Jenis/Jabatan Dosen', 'value' => $row->jenis ?? 'D'],
['name' => 'password', 'label' => 'Password', 'type' => 'password', 'required' => ! $editing, 'help' => $editing ? 'Kosongkan jika tidak mengganti password.' : 'Password awal dosen.'],
['name' => 'status', 'label' => 'Status', 'type' => 'select', 'value' => $row->status ?? 'A', 'options' => ['A' => 'Aktif', 'P' => 'Pending', 'N' => 'Nonaktif']],
];
}
private function kkFields(?object $row = null): array
{
$dosen = DB::table('tbdosen')->select(['iddosen', 'nmLengkap'])->orderBy('nmLengkap')->get();
$options = ['' => '- Pilih Dosen -'];
foreach ($dosen as $item) {
$options[$item->iddosen] = $item->nmLengkap;
}
return [
['name' => 'namaKK', 'label' => 'Nama Kelompok Keahlian', 'value' => $row->namaKK ?? '', 'required' => true],
['name' => 'warnaLabel', 'label' => 'Warna Label', 'value' => $row->warnaLabel ?? '', 'help' => 'Opsional, mengikuti data legacy.'],
['name' => 'ketuaKK', 'label' => 'Ketua KK', 'type' => 'select', 'value' => $row->ketuaKK ?? '', 'options' => $options],
['name' => 'sekretarisKK', 'label' => 'Sekretaris KK', 'type' => 'select', 'value' => $row->sekretarisKK ?? '', 'options' => $options],
];
}
private function praoutlineBase(array $user)
{
return DB::table('tbpraoutline as tp')
->leftJoin('tbmhs as tm', 'tp.nim', '=', 'tm.nim')
->select(['tp.id', 'tp.nim', 'tm.nmLengkap as mahasiswa', 'tp.judul', 'tp.tgl_upload', 'tp.wkt_upload', 'tp.status_usulan'])
->where('tp.idProdi', $user['prodi']);
}
private function user(Request $request): array
{
$auth = $request->session()->get('legacy_auth');
abort_unless(($auth['role'] ?? null) === 'admin', 403);
return $auth['user'];
}
private function abortIfSuper(array $user): void
{
abort_if(($user['lvl'] ?? null) === 'S', 403);
}
private function abortUnlessSuper(array $user): void
{
abort_unless(($user['lvl'] ?? null) === 'S', 403);
}
private function statusLabel(?string $status): string
{
return match ($status) {
'0' => 'Dalam Review',
'1' => 'Disetujui',
'2' => 'Ditolak',
default => $status ?? '-',
};
}
private function formatDateTime(?string $value): string
{
if (! $value || trim($value) === '') {
return '-';
}
return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i');
}
}