Initial commit for combined ProjectKP
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use App\Models\Penugasan;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
use Maatwebsite\Excel\Concerns\WithDrawings;
|
||||
use Maatwebsite\Excel\Concerns\WithEvents;
|
||||
use Maatwebsite\Excel\Events\AfterSheet;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
|
||||
class PenugasanExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize, WithDrawings, WithEvents
|
||||
{
|
||||
protected $columns;
|
||||
|
||||
public function __construct(array $columns = [])
|
||||
{
|
||||
$this->columns = $columns;
|
||||
}
|
||||
|
||||
public function collection()
|
||||
{
|
||||
return Penugasan::with(['petugas', 'pembuat'])->latest()->get();
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
if (empty($this->columns)) {
|
||||
return [
|
||||
'No', 'Nomor Surat', 'Kegiatan', 'Lokasi', 'Keterangan',
|
||||
'Tanggal Mulai', 'Tanggal Selesai', 'Status', 'Catatan Status',
|
||||
'Daftar Petugas', 'Dibuat Oleh', 'Dokumentasi'
|
||||
];
|
||||
}
|
||||
|
||||
$headings = ['No'];
|
||||
$map = [
|
||||
'nomor_surat' => 'Nomor Surat',
|
||||
'kegiatan' => 'Nama Kegiatan',
|
||||
'lokasi' => 'Lokasi',
|
||||
'keterangan' => 'Deskripsi',
|
||||
'tanggal_mulai' => 'Tanggal Pelaksanaan',
|
||||
'status' => 'Status',
|
||||
'status_note' => 'Catatan Status',
|
||||
'petugas' => 'Petugas',
|
||||
'pembuat' => 'Dibuat Oleh',
|
||||
'dokumentasi' => 'Dokumentasi'
|
||||
];
|
||||
|
||||
foreach ($this->columns as $col) {
|
||||
if (isset($map[$col])) {
|
||||
$headings[] = $map[$col];
|
||||
}
|
||||
}
|
||||
|
||||
return $headings;
|
||||
}
|
||||
|
||||
public function map($penugasan): array
|
||||
{
|
||||
static $no = 1;
|
||||
|
||||
if (empty($this->columns)) {
|
||||
$namaPetugas = $penugasan->petugas->map(function($p) {
|
||||
return $p->name ?? $p->username;
|
||||
})->implode(', ');
|
||||
|
||||
return [
|
||||
$no++,
|
||||
$penugasan->nomor_surat ?? '-',
|
||||
$penugasan->kegiatan,
|
||||
$penugasan->lokasi ?? '-',
|
||||
$penugasan->keterangan ?? '-',
|
||||
$penugasan->tanggal_mulai,
|
||||
$penugasan->tanggal_selesai ?? '-',
|
||||
strtoupper($penugasan->status),
|
||||
$penugasan->status_note ?? '-',
|
||||
$namaPetugas ?: 'Belum ada petugas',
|
||||
$penugasan->pembuat->name ?? 'System',
|
||||
$penugasan->lampiran ? '' : 'Tidak ada'
|
||||
];
|
||||
}
|
||||
|
||||
$row = [$no++];
|
||||
|
||||
foreach ($this->columns as $col) {
|
||||
switch ($col) {
|
||||
case 'nomor_surat':
|
||||
$row[] = $penugasan->nomor_surat ?? '-';
|
||||
break;
|
||||
case 'kegiatan':
|
||||
$row[] = $penugasan->kegiatan;
|
||||
break;
|
||||
case 'lokasi':
|
||||
$row[] = $penugasan->lokasi ?? '-';
|
||||
break;
|
||||
case 'keterangan':
|
||||
$row[] = $penugasan->keterangan ?? '-';
|
||||
break;
|
||||
case 'tanggal_mulai':
|
||||
$row[] = $penugasan->tanggal_mulai;
|
||||
break;
|
||||
case 'status':
|
||||
$row[] = strtoupper($penugasan->status);
|
||||
break;
|
||||
case 'status_note':
|
||||
$row[] = $penugasan->status_note ?? '-';
|
||||
break;
|
||||
case 'petugas':
|
||||
$namaPetugas = $penugasan->petugas->map(function($p) {
|
||||
return $p->name ?? $p->username;
|
||||
})->implode(', ');
|
||||
$row[] = $namaPetugas ?: 'Belum ada petugas';
|
||||
break;
|
||||
case 'pembuat':
|
||||
$row[] = $penugasan->pembuat->name ?? 'System';
|
||||
break;
|
||||
case 'dokumentasi':
|
||||
$row[] = $penugasan->lampiran ? '' : 'Tidak ada';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function drawings()
|
||||
{
|
||||
$drawings = [];
|
||||
$rowNum = 2; // Data starts at row 2
|
||||
$penugasans = $this->collection();
|
||||
|
||||
$headings = $this->headings();
|
||||
$colIndex = array_search('Dokumentasi', $headings);
|
||||
|
||||
if ($colIndex !== false) {
|
||||
$colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colIndex + 1); // +1 because Excel is 1-based, array_search is 0-based
|
||||
|
||||
foreach ($penugasans as $penugasan) {
|
||||
if ($penugasan->lampiran && \Storage::disk('public')->exists($penugasan->lampiran)) {
|
||||
$drawing = new Drawing();
|
||||
$drawing->setName('Dokumentasi');
|
||||
$drawing->setDescription('Dokumentasi');
|
||||
$drawing->setPath(storage_path('app/public/' . $penugasan->lampiran));
|
||||
$drawing->setHeight(70);
|
||||
$drawing->setCoordinates($colLetter . $rowNum);
|
||||
$drawing->setOffsetX(5);
|
||||
$drawing->setOffsetY(5);
|
||||
$drawings[] = $drawing;
|
||||
}
|
||||
$rowNum++;
|
||||
}
|
||||
}
|
||||
|
||||
return $drawings;
|
||||
}
|
||||
|
||||
public function registerEvents(): array
|
||||
{
|
||||
return [
|
||||
AfterSheet::class => function(AfterSheet $event) {
|
||||
$penugasans = $this->collection();
|
||||
$rowNum = 2;
|
||||
|
||||
$headings = $this->headings();
|
||||
$hasDokumentasi = in_array('Dokumentasi', $headings);
|
||||
|
||||
if ($hasDokumentasi) {
|
||||
foreach ($penugasans as $penugasan) {
|
||||
if ($penugasan->lampiran && \Storage::disk('public')->exists($penugasan->lampiran)) {
|
||||
$event->sheet->getDelegate()->getRowDimension($rowNum)->setRowHeight(60);
|
||||
}
|
||||
$rowNum++;
|
||||
}
|
||||
|
||||
$colIndex = array_search('Dokumentasi', $headings);
|
||||
$colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colIndex + 1);
|
||||
$event->sheet->getDelegate()->getColumnDimension($colLetter)->setWidth(20);
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class NotificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mendapatkan daftar notifikasi user saat ini.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Ambil notifikasi (semua), urutkan yang terbaru, batasi 20
|
||||
$notifications = $user->notifications()->latest()->take(20)->get();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $notifications
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menandai semua notifikasi sebagai telah dibaca.
|
||||
*/
|
||||
public function markAsRead()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$user->unreadNotifications->markAsRead();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Semua notifikasi ditandai telah dibaca.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Penugasan;
|
||||
use App\Models\User;
|
||||
use App\Exports\PenugasanExport;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use App\Notifications\PenugasanStatusNotification;
|
||||
|
||||
class PenugasanController extends Controller
|
||||
{
|
||||
/**
|
||||
* 1. DASHBOARD STATS
|
||||
*/
|
||||
public function dashboard()
|
||||
{
|
||||
try {
|
||||
$user = Auth::user();
|
||||
|
||||
$query = Penugasan::query()->where('is_archived', false);
|
||||
|
||||
if ($user && $user->role === 'user') {
|
||||
$query->whereHas('petugas', function($q) use ($user) {
|
||||
$q->where('users.id', $user->id);
|
||||
});
|
||||
}
|
||||
|
||||
$totalTugas = (clone $query)->where('status', '!=', 'dibatalkan')->count();
|
||||
$proses = (clone $query)->where('status', 'proses')->count();
|
||||
$selesai = (clone $query)->where('status', 'selesai')->count();
|
||||
$telahDitinjau = (clone $query)->where('status', 'telah_ditinjau')->count();
|
||||
$dibatalkan = (clone $query)->where('status', 'dibatalkan')->count();
|
||||
|
||||
// Ambil 5 tugas terbaru beserta data petugasnya, kecuali yang dibatalkan
|
||||
$terbaru = (clone $query)->with('petugas')->where('status', '!=', 'dibatalkan')->latest()->limit(5)->get();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'stats' => [
|
||||
'total_tugas' => $totalTugas,
|
||||
'status_proses' => $proses,
|
||||
'status_selesai' => $selesai,
|
||||
'status_ditinjau' => $telahDitinjau,
|
||||
'status_batal' => $dibatalkan,
|
||||
],
|
||||
'recent_activities' => $terbaru
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. LIST SEMUA TUGAS
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$query = Penugasan::with(['petugas', 'pembuat', 'progress.user']);
|
||||
|
||||
if ($user && $user->role === 'user') {
|
||||
$query->whereHas('petugas', function($q) use ($user) {
|
||||
$q->where('users.id', $user->id);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->has('is_archived') && $request->is_archived == 'true') {
|
||||
$query->where('is_archived', true);
|
||||
} else {
|
||||
$query->where('is_archived', false);
|
||||
}
|
||||
|
||||
$data = $query->latest()->get();
|
||||
return response()->json(['success' => true, 'data' => $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. SIMPAN TUGAS BARU (SINKRON DENGAN FRONTEND)
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'nomor_surat' => 'nullable|string|unique:penugasan,nomor_surat',
|
||||
'kegiatan' => 'required|string',
|
||||
'tanggal_mulai' => 'required|date',
|
||||
'pegawai_ids' => 'required|array|min:1',
|
||||
], [
|
||||
'nomor_surat.unique' => 'Nomor surat ini sudah terdaftar di sistem (termasuk yang sudah dihapus/dibatalkan). Harap gunakan nomor yang berbeda.',
|
||||
'pegawai_ids.required' => 'Pilih minimal satu petugas.',
|
||||
'pegawai_ids.min' => 'Pilih minimal satu petugas.',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
|
||||
$adminId = Auth::id() ?? 1;
|
||||
|
||||
$lampiranPath = null;
|
||||
if ($request->hasFile('lampiran')) {
|
||||
$lampiranPath = $request->file('lampiran')->store('lampiran', 'public');
|
||||
}
|
||||
|
||||
$penugasan = Penugasan::create([
|
||||
'nomor_surat' => $request->nomor_surat,
|
||||
'kegiatan' => $request->kegiatan,
|
||||
'lokasi' => $request->lokasi,
|
||||
'keterangan' => $request->keterangan,
|
||||
'lampiran' => $lampiranPath,
|
||||
'tanggal_mulai' => $request->tanggal_mulai,
|
||||
'tanggal_selesai' => $request->tanggal_selesai,
|
||||
'status_note' => $request->status_note,
|
||||
'status' => 'proses',
|
||||
'created_by' => $adminId,
|
||||
]);
|
||||
|
||||
// SIMPAN MULTIPLE PEGAWAI
|
||||
$penugasan->petugas()->sync($request->pegawai_ids);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Penugasan berhasil dibuat!',
|
||||
'data' => $penugasan->load('petugas')
|
||||
], 201);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
DB::rollback();
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 3b. SHOW DETAIL TUGAS
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
try {
|
||||
$penugasan = Penugasan::with(['petugas', 'pembuat', 'penyetuju', 'progress.user'])->findOrFail($id);
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $penugasan
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Data tidak ditemukan: ' . $e->getMessage()], 404);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 3c. UPDATE TUGAS
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'nomor_surat' => 'nullable|string|unique:penugasan,nomor_surat,' . $id,
|
||||
'kegiatan' => 'required|string',
|
||||
'tanggal_mulai' => 'required|date',
|
||||
'pegawai_ids' => 'required|array|min:1',
|
||||
], [
|
||||
'nomor_surat.unique' => 'Nomor surat ini sudah terdaftar di sistem (termasuk yang sudah dihapus/dibatalkan). Harap gunakan nomor yang berbeda.',
|
||||
'pegawai_ids.required' => 'Pilih minimal satu petugas.',
|
||||
'pegawai_ids.min' => 'Pilih minimal satu petugas.',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$penugasan = Penugasan::findOrFail($id);
|
||||
|
||||
$lampiranPath = $penugasan->lampiran;
|
||||
if ($request->hasFile('lampiran')) {
|
||||
if ($lampiranPath && Storage::disk('public')->exists($lampiranPath)) {
|
||||
Storage::disk('public')->delete($lampiranPath);
|
||||
}
|
||||
$lampiranPath = $request->file('lampiran')->store('lampiran', 'public');
|
||||
}
|
||||
|
||||
$penugasan->update([
|
||||
'nomor_surat' => $request->nomor_surat,
|
||||
'kegiatan' => $request->kegiatan,
|
||||
'lokasi' => $request->lokasi,
|
||||
'keterangan' => $request->keterangan,
|
||||
'lampiran' => $lampiranPath,
|
||||
'tanggal_mulai' => $request->tanggal_mulai,
|
||||
'tanggal_selesai' => $request->tanggal_selesai,
|
||||
'status_note' => $request->status_note,
|
||||
// tidak update status/created_by
|
||||
]);
|
||||
|
||||
$penugasan->petugas()->sync($request->pegawai_ids);
|
||||
|
||||
DB::commit();
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Penugasan berhasil diperbarui!',
|
||||
'data' => $penugasan->load('petugas')
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. UPDATE STATUS (TINJAU)
|
||||
*/
|
||||
public function updateStatus(Request $request, $id)
|
||||
{
|
||||
try {
|
||||
$penugasan = Penugasan::findOrFail($id);
|
||||
|
||||
if ($request->status == 'telah_ditinjau') {
|
||||
$penugasan->update([
|
||||
'status' => 'telah_ditinjau',
|
||||
'approved_by' => Auth::id() ?? $request->approved_by,
|
||||
'approved_at' => now(),
|
||||
'status_note' => $request->has('status_note') ? $request->status_note : null
|
||||
]);
|
||||
} else {
|
||||
$updateData = ['status' => $request->status];
|
||||
if ($request->has('status_note')) {
|
||||
$updateData['status_note'] = $request->status_note;
|
||||
}
|
||||
$penugasan->update($updateData);
|
||||
|
||||
// Kirim notifikasi
|
||||
if (in_array($request->status, ['selesai', 'dibatalkan', 'proses'])) {
|
||||
$action = 'diperbarui';
|
||||
if ($request->status === 'selesai') $action = 'disetujui';
|
||||
if ($request->status === 'dibatalkan') $action = 'dibatalkan';
|
||||
if ($request->status === 'proses') $action = 'dikembalikan (revisi)';
|
||||
|
||||
if ($penugasan->petugas->isNotEmpty()) {
|
||||
Notification::send($penugasan->petugas, new PenugasanStatusNotification($penugasan, $request->status, $action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Status diperbarui!']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. BATALKAN TUGAS
|
||||
*/
|
||||
public function destroy(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'alasan' => 'required|string' // Frontend kirim 'alasan'
|
||||
]);
|
||||
|
||||
try {
|
||||
$penugasan = Penugasan::findOrFail($id);
|
||||
|
||||
$penugasan->update([
|
||||
'status' => 'dibatalkan',
|
||||
'alasan_hapus' => $request->alasan
|
||||
]);
|
||||
|
||||
if ($penugasan->petugas->isNotEmpty()) {
|
||||
Notification::send($penugasan->petugas, new PenugasanStatusNotification($penugasan, 'dibatalkan', 'dihapus'));
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Kegiatan berhasil dibatalkan.'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 6. NOTIFIKASI
|
||||
*/
|
||||
public function notifications()
|
||||
{
|
||||
// Contoh implementasi: kirim kegiatan yang sudah 'telah_ditinjau' untuk user ini
|
||||
try {
|
||||
$user = Auth::user();
|
||||
$notifications = [];
|
||||
|
||||
if ($user && $user->role === 'user') {
|
||||
// Pegawai id mungkin sama dengan user_id
|
||||
$pegawai = \App\Models\Pegawai::where('user_id', $user->id)->first();
|
||||
if ($pegawai) {
|
||||
$notifications = $pegawai->penugasan()
|
||||
->where('status', 'telah_ditinjau')
|
||||
->orderBy('approved_at', 'desc')
|
||||
->limit(5)
|
||||
->get()
|
||||
->map(function($p) {
|
||||
return [
|
||||
'id' => $p->id,
|
||||
'text' => "Penugasan '{$p->kegiatan}' telah ditinjau.",
|
||||
'is_read' => false
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $notifications
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 7. EXCEL EXPORT
|
||||
*/
|
||||
public function export(Request $request)
|
||||
{
|
||||
try {
|
||||
$columns = $request->input('columns', []);
|
||||
return Excel::download(new PenugasanExport($columns), 'Laporan_Penugasan.xlsx');
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Export Gagal: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleArchive($id)
|
||||
{
|
||||
try {
|
||||
$penugasan = Penugasan::findOrFail($id);
|
||||
$penugasan->is_archived = !$penugasan->is_archived;
|
||||
$penugasan->save();
|
||||
|
||||
$status = $penugasan->is_archived ? 'diarsipkan' : 'dikembalikan dari arsip';
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => "Laporan berhasil $status."
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Gagal mengarsipkan laporan: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function storeProgress(Request $request, $id)
|
||||
{
|
||||
try {
|
||||
$request->validate([
|
||||
'note' => 'required|string',
|
||||
'attachment' => 'nullable|file|mimes:jpg,jpeg,png,pdf|max:2048'
|
||||
]);
|
||||
|
||||
$penugasan = Penugasan::findOrFail($id);
|
||||
|
||||
$path = null;
|
||||
if ($request->hasFile('attachment')) {
|
||||
$file = $request->file('attachment');
|
||||
$filename = time() . '_' . $file->getClientOriginalName();
|
||||
$file->move(public_path('uploads/progress'), $filename);
|
||||
$path = 'uploads/progress/' . $filename;
|
||||
}
|
||||
|
||||
$progress = $penugasan->progress()->create([
|
||||
'user_id' => Auth::id(),
|
||||
'note' => $request->note,
|
||||
'attachment' => $path
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Progress log added successfully',
|
||||
'data' => $progress->load('user')
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Failed to add progress: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Models\User;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* LOGIN API
|
||||
* Menerima username/email + password.
|
||||
* Mengembalikan token untuk dipakai frontend KP-ASOY.
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'username' => 'nullable|string',
|
||||
'login' => 'nullable|string',
|
||||
'password' => 'required|string',
|
||||
]);
|
||||
|
||||
// Frontend KP-ASOY mengirim field "username".
|
||||
// Tapi tetap kita dukung field "login" agar fleksibel.
|
||||
$login = trim($request->username ?? $request->login);
|
||||
|
||||
if (!$login) {
|
||||
return response()->json([
|
||||
'message' => 'Username atau email wajib diisi.'
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Cek apakah input berupa email atau username
|
||||
$field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
|
||||
|
||||
$user = User::where($field, $login)->first();
|
||||
|
||||
\Illuminate\Support\Facades\Log::info('Login Attempt', ['login' => $login, 'password' => $request->password, 'user_found' => $user ? true : false, 'hash_check' => $user ? Hash::check($request->password, $user->password) : false]);
|
||||
|
||||
if (!$user || !Hash::check($request->password, $user->password)) {
|
||||
return response()->json([
|
||||
'message' => 'Login gagal. Username/email atau password salah.'
|
||||
], 401);
|
||||
}
|
||||
|
||||
// Hapus token lama agar tidak menumpuk terlalu banyak
|
||||
$user->tokens()->delete();
|
||||
|
||||
// Buat token Sanctum
|
||||
$token = $user->createToken('kp-asoy-token')->plainTextToken;
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Login berhasil',
|
||||
'token' => $token,
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'username' => $user->username,
|
||||
'email' => $user->email,
|
||||
'role' => strtolower($user->role),
|
||||
'jabatan' => $user->jabatan,
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* REGISTER USER
|
||||
* Dipakai admin/superadmin untuk membuat akun user/pegawai.
|
||||
*/
|
||||
public function register(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'username' => 'required|string|max:255|unique:users,username',
|
||||
'password' => 'required|string|min:6',
|
||||
'role' => [
|
||||
'required',
|
||||
Rule::in(['user', 'admin', 'superadmin', 'User', 'Admin', 'Superadmin', 'Super Admin'])
|
||||
],
|
||||
'jabatan' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$role = strtolower($request->role);
|
||||
|
||||
if ($role === 'super admin') {
|
||||
$role = 'superadmin';
|
||||
}
|
||||
|
||||
// Generate email if not provided to satisfy DB unique constraint
|
||||
$email = $request->email ?? ($request->username . '@mail.com');
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'username' => $request->username,
|
||||
'email' => $email,
|
||||
'password' => bcrypt($request->password),
|
||||
'role' => $role,
|
||||
'jabatan' => $request->jabatan,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Register berhasil',
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'username' => $user->username,
|
||||
'email' => $user->email,
|
||||
'role' => $user->role,
|
||||
'jabatan' => $user->jabatan,
|
||||
]
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* DATA USER YANG SEDANG LOGIN
|
||||
*/
|
||||
public function me(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
return response()->json([
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'username' => $user->username,
|
||||
'email' => $user->email,
|
||||
'role' => strtolower($user->role),
|
||||
'jabatan' => $user->jabatan,
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* LOGOUT API
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
if ($request->user()) {
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Logout berhasil'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* UPDATE AKUN
|
||||
* Untuk fitur pengaturan akun: ubah email dan password.
|
||||
*/
|
||||
public function updateProfile(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$request->validate([
|
||||
'username' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::unique('users', 'username')->ignore($user->id),
|
||||
],
|
||||
'password' => 'nullable|string|min:6',
|
||||
'current_password' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$user->username = $request->username;
|
||||
// Jika tidak ada email, tidak perlu diupdate.
|
||||
// Backend tetap aman karena saat register kita yang men-generate email.
|
||||
|
||||
if ($request->filled('password')) {
|
||||
if ($user->role === 'user') {
|
||||
return response()->json([
|
||||
'message' => 'Pegawai tidak diizinkan mengubah kata sandi.'
|
||||
], 403);
|
||||
}
|
||||
if (!Hash::check($request->current_password, $user->password)) {
|
||||
return response()->json([
|
||||
'message' => 'Kata sandi saat ini tidak cocok.'
|
||||
], 400);
|
||||
}
|
||||
$user->password = bcrypt($request->password);
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Profil berhasil diperbarui',
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'username' => $user->username,
|
||||
'email' => $user->email,
|
||||
'role' => strtolower($user->role),
|
||||
'jabatan' => $user->jabatan,
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Pegawai;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class PegawaiController extends Controller
|
||||
{
|
||||
// 1. Tampil Semua Data
|
||||
public function index()
|
||||
{
|
||||
$data = Pegawai::with('user:id,email,role')->get();
|
||||
return response()->json(['status' => true, 'data' => $data], 200);
|
||||
}
|
||||
|
||||
// 2. Simpan Data Baru (Oleh Admin/Super Admin)
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'password' => 'required|string|min:6',
|
||||
'nip' => 'required|string|unique:pegawai,nip',
|
||||
'nama' => 'required|string|max:255',
|
||||
'jabatan' => 'required|string|max:255',
|
||||
'role' => 'nullable|in:super_admin,admin,pegawai',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['status' => false, 'errors' => $validator->errors()], 422);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role ?? 'pegawai',
|
||||
]);
|
||||
|
||||
Pegawai::create([
|
||||
'nip' => $request->nip,
|
||||
'nama' => $request->nama,
|
||||
'jabatan' => $request->jabatan,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
return response()->json(['status' => true, 'message' => 'Pegawai berhasil ditambah'], 201);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['status' => false, 'error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Lihat Detail
|
||||
public function show($id)
|
||||
{
|
||||
$pegawai = Pegawai::with('user:id,email,role')->find($id);
|
||||
if (!$pegawai) return response()->json(['status' => false, 'message' => 'Data tidak ditemukan'], 404);
|
||||
return response()->json(['status' => true, 'data' => $pegawai], 200);
|
||||
}
|
||||
|
||||
// 4. Update Data
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$pegawai = Pegawai::find($id);
|
||||
if (!$pegawai) return response()->json(['message' => 'Data tidak ditemukan'], 404);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$pegawai->update([
|
||||
'nip' => $request->nip ?? $pegawai->nip,
|
||||
'nama' => $request->nama ?? $pegawai->nama,
|
||||
'jabatan' => $request->jabatan ?? $pegawai->jabatan,
|
||||
]);
|
||||
|
||||
$user = User::find($pegawai->user_id);
|
||||
if ($user) {
|
||||
$user->update([
|
||||
'name' => $request->nama ?? $user->name,
|
||||
'role' => $request->role ?? $user->role,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return response()->json(['status' => true, 'message' => 'Data berhasil diupdate']);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['status' => false, 'error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. HAPUS PEGAWAI (REVISI SESUAI REQUEST DOSEN)
|
||||
* Ditambahkan input 'alasan_hapus'
|
||||
*/
|
||||
public function destroy(Request $request, $id)
|
||||
{
|
||||
$pegawai = Pegawai::find($id);
|
||||
if (!$pegawai) return response()->json(['message' => 'Data tidak ditemukan'], 404);
|
||||
|
||||
// Validasi alasan hapus (Request Dosen)
|
||||
$request->validate([
|
||||
'alasan_hapus' => 'required|string'
|
||||
]);
|
||||
|
||||
// Simpan alasan ke database sebelum di-soft delete
|
||||
$pegawai->update([
|
||||
'alasan_hapus' => $request->alasan_hapus
|
||||
]);
|
||||
|
||||
$pegawai->delete();
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Pegawai berhasil dipindahkan ke tempat sampah dengan alasan: ' . $request->alasan_hapus
|
||||
]);
|
||||
}
|
||||
|
||||
// 6. Melihat "Tong Sampah" (Sesuai request dosen: Dashboard tidak perlu detail, detail di sini)
|
||||
public function trash()
|
||||
{
|
||||
// Ambil data yang sudah di-soft delete beserta alasan hapusnya
|
||||
$pegawaiTerhapus = Pegawai::onlyTrashed()->get();
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'data' => $pegawaiTerhapus
|
||||
]);
|
||||
}
|
||||
|
||||
// 7. Restore
|
||||
public function restore($id)
|
||||
{
|
||||
$pegawai = Pegawai::withTrashed()->find($id);
|
||||
|
||||
if ($pegawai && $pegawai->trashed()) {
|
||||
// Kosongkan kembali alasan hapus saat di-restore
|
||||
$pegawai->update(['alasan_hapus' => null]);
|
||||
$pegawai->restore();
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Data pegawai ' . $pegawai->nama . ' berhasil dikembalikan!'
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['status' => false, 'message' => 'Data tidak ditemukan di tong sampah'], 404);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\UnitKerja;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UnitKerjaController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = UnitKerja::all();
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'nama_unit' => 'required|unique:unit_kerja,nama_unit',
|
||||
]);
|
||||
|
||||
$unit = UnitKerja::create($request->all());
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Unit Kerja berhasil ditambah',
|
||||
'data' => $unit
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$unit = UnitKerja::find($id);
|
||||
if (!$unit) return response()->json(['message' => 'Data tidak ditemukan'], 404);
|
||||
|
||||
return response()->json(['success' => true, 'data' => $unit]);
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$unit = UnitKerja::find($id);
|
||||
if (!$unit) return response()->json(['message' => 'Data tidak ditemukan'], 404);
|
||||
|
||||
$unit->update($request->all());
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Unit Kerja berhasil diupdate'
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$unit = UnitKerja::find($id);
|
||||
if (!$unit) return response()->json(['message' => 'Data tidak ditemukan'], 404);
|
||||
|
||||
$unit->delete();
|
||||
return response()->json(['success' => true, 'message' => 'Unit Kerja dihapus']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckRole
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Closure(Request): (Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$roles)
|
||||
{
|
||||
// Cek apakah user sudah login dan rolenya ada di daftar yang diizinkan
|
||||
if (!$request->user() || !in_array($request->user()->role, $roles)) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Akses ditolak! Anda tidak punya hak akses untuk aksi ini.'
|
||||
], 403); // 403 artinya Forbidden
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Pegawai extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'pegawai';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'nip',
|
||||
'nama',
|
||||
'jabatan',
|
||||
'user_id'
|
||||
];
|
||||
|
||||
// 1. Relasi ke User (Sudah ada)
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. TAMBAHKAN INI: Relasi ke Penugasan (Many-to-Many)
|
||||
* Ini supaya Pegawai tahu dia punya banyak tugas melalui tabel pivot
|
||||
*/
|
||||
public function penugasan()
|
||||
{
|
||||
return $this->belongsToMany(Penugasan::class, 'penugasan_pegawai', 'pegawai_id', 'penugasan_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes; // WAJIB UNTUK SOFT DELETES
|
||||
|
||||
class Penugasan extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes; // Gunakan trait SoftDeletes di sini
|
||||
|
||||
// REVISI: Paksa nama tabel menjadi 'penugasan' (tanpa s) sesuai keinginanmu
|
||||
protected $table = 'penugasan';
|
||||
|
||||
protected $fillable = [
|
||||
'nomor_surat',
|
||||
'kegiatan',
|
||||
'lokasi',
|
||||
'keterangan',
|
||||
'lampiran',
|
||||
'tanggal_mulai',
|
||||
'tanggal_selesai',
|
||||
'status', // Isinya nanti: 'proses', 'selesai', 'telah_ditinjau'
|
||||
'status_note', // Catatan progres dari pegawai
|
||||
'is_archived',
|
||||
'created_by',
|
||||
'approved_by',
|
||||
'approved_at',
|
||||
'alasan_hapus' // TAMBAHAN: Kolom untuk menyimpan alasan hapus sesuai saran KP
|
||||
];
|
||||
|
||||
// Mengatur kolom deleted_at agar dikenali sebagai instance Carbon/Tanggal
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
// 1. Relasi ke Pegawai (Pivot: penugasan_pegawai)
|
||||
public function petugas()
|
||||
{
|
||||
// Sesuaikan nama tabel pivot jika kamu juga mengubahnya nanti
|
||||
return $this->belongsToMany(User::class, 'penugasan_pegawai', 'penugasan_id', 'pegawai_id');
|
||||
}
|
||||
|
||||
public function progress()
|
||||
{
|
||||
return $this->hasMany(PenugasanProgress::class)->latest();
|
||||
}
|
||||
|
||||
// 2. Relasi ke Pembuat (Admin/Kasi)
|
||||
public function pembuat()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
// 3. Relasi ke Penyetuju (Superadmin/Kabid)
|
||||
public function penyetuju()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'approved_by');
|
||||
}
|
||||
|
||||
// 4. Accessor untuk Status Dinamis (Otomatis Terlambat jika melewati deadline)
|
||||
public function getStatusAttribute($value)
|
||||
{
|
||||
if ($value === 'proses' && !empty($this->attributes['tanggal_selesai'])) {
|
||||
if (\Carbon\Carbon::now()->startOfDay()->gt(\Carbon\Carbon::parse($this->attributes['tanggal_selesai'])->startOfDay())) {
|
||||
return 'terlambat';
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PenugasanProgress extends Model
|
||||
{
|
||||
protected $table = 'penugasan_progress';
|
||||
protected $fillable = ['penugasan_id', 'user_id', 'note', 'attachment'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\Pegawai;
|
||||
|
||||
class UnitKerja extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'unit_kerja'; // sesuaikan dengan nama tabel di migration
|
||||
|
||||
protected $fillable = [
|
||||
'nama_unit'
|
||||
];
|
||||
|
||||
// 🔗 Relasi: 1 unit kerja punya banyak pegawai
|
||||
public function pegawai()
|
||||
{
|
||||
return $this->hasMany(Pegawai::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// Import yang diperlukan
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* Kolom yang bisa diisi secara massal.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'password',
|
||||
'role',
|
||||
'jabatan',
|
||||
];
|
||||
|
||||
/**
|
||||
* Kolom yang disembunyikan saat data dikonversi ke JSON.
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Casting tipe data kolom.
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| RELASI ANTAR TABEL
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Relasi 1 User ke 1 Pegawai
|
||||
*/
|
||||
public function pegawai()
|
||||
{
|
||||
return $this->hasOne(Pegawai::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* REVISI DI SINI:
|
||||
* Relasi agar Pegawai bisa melihat daftar tugas yang diberikan kepadanya.
|
||||
*/
|
||||
public function penugasan()
|
||||
{
|
||||
return $this->belongsToMany(Penugasan::class, 'penugasan_pegawai', 'pegawai_id', 'penugasan_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relasi ke Penugasan (User sebagai pembuat)
|
||||
*/
|
||||
public function penugasanDibuat()
|
||||
{
|
||||
return $this->hasMany(Penugasan::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relasi ke Penugasan (User sebagai penyetuju/admin)
|
||||
*/
|
||||
public function penugasanDisetujui()
|
||||
{
|
||||
return $this->hasMany(Penugasan::class, 'approved_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class PenugasanStatusNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
protected $penugasan;
|
||||
protected $status;
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct($penugasan, $status, $action = 'diperbarui')
|
||||
{
|
||||
$this->penugasan = $penugasan;
|
||||
$this->status = $status;
|
||||
$this->action = $action; // 'disetujui', 'dihapus', 'ditolak'
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toDatabase(object $notifiable): array
|
||||
{
|
||||
$kegiatan = $this->penugasan->kegiatan ?? 'Kegiatan Anda';
|
||||
|
||||
if ($this->action === 'dihapus') {
|
||||
$message = "Laporan kegiatan '{$kegiatan}' telah Dihapus oleh Admin.";
|
||||
} elseif ($this->action === 'disetujui') {
|
||||
$message = "Hore! Laporan kegiatan '{$kegiatan}' telah Disetujui (Selesai).";
|
||||
} elseif ($this->action === 'ditolak') {
|
||||
$message = "Maaf, Laporan kegiatan '{$kegiatan}' Ditolak. Silakan periksa kembali.";
|
||||
} else {
|
||||
$message = "Status laporan kegiatan '{$kegiatan}' diubah menjadi: {$this->status}.";
|
||||
}
|
||||
|
||||
return [
|
||||
'penugasan_id' => $this->penugasan->id ?? null,
|
||||
'status' => $this->status,
|
||||
'action' => $this->action,
|
||||
'message' => $message,
|
||||
'kegiatan' => $kegiatan
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user