391 lines
14 KiB
PHP
391 lines
14 KiB
PHP
<?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);
|
|
}
|
|
}
|
|
}
|