Controller

B.M. Restu Pandhu P.

/ 9

1

AdminUserController

<?php

namespace App\Http\Controllers\Admin;

use App\Actions\User\CreateStaffAccountAction;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreStaffRequest;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;

class AdminUserController extends Controller
{
    public function index(): View
    {
        return view('admin.admins.index', [
            'admins' => User::query()->where('role', 'admin')->orderBy('name')->get(),
        ]);
    }

    public function store(StoreStaffRequest $request, CreateStaffAccountAction $action): RedirectResponse
    {
        $action->handle($request->validated(), 'admin');

        return back()->with('success', 'Akun admin dibuat. Email berisi password login sudah dikirim.');
    }

    public function update(StoreStaffRequest $request, User $admin): RedirectResponse
    {
        $admin->update($request->safe()->only(['name', 'username', 'email']));

        return back()->with('success', 'Data admin diperbarui.');
    }

    /**
     * Toggle active/inactive, guarding against deactivating the last active
     * admin (the system would then have no one left to manage it).
     */
    public function toggleActive(User $admin): RedirectResponse
    {
        if ($admin->is_active) {
            $otherActiveAdmins = User::query()
                ->where('role', 'admin')
                ->where('is_active', true)
                ->where('id', '!=', $admin->id)
                ->exists();

            if (! $otherActiveAdmins) {
                return back()->with('error', 'Tidak bisa menonaktifkan admin aktif terakhir.');
            }
        }

        $admin->update(['is_active' => ! $admin->is_active]);

        return back()->with('success', $admin->is_active ? 'Akun admin diaktifkan.' : 'Akun admin dinonaktifkan.');
    }
}

ApplicationController

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\StoreApplicationRequest;
use App\Models\Application;
use App\Models\Client;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;

class ApplicationController extends Controller
{
    public function index(): View
    {
        return view('admin.applications.index', [
            'applications' => Application::query()->with(['client', 'dedicatedTechnician'])->orderBy('name')->get(),
            'clients' => Client::query()->orderBy('name')->get(),
            'technicians' => User::query()->where('role', 'technician')->where('is_active', true)->orderBy('name')->get(),
        ]);
    }

    public function store(StoreApplicationRequest $request): RedirectResponse
    {
        Application::create($request->validated() + ['is_active' => true]);

        return back()->with('success', 'Aplikasi baru ditambahkan.');
    }

    public function update(StoreApplicationRequest $request, Application $application): RedirectResponse
    {
        $application->update($request->validated());

        return back()->with('success', 'Data aplikasi diperbarui.');
    }

    /**
     * Deactivate instead of delete: past complaints tied to this application
     * must stay intact (same reasoning as Client::toggleActive, §11/§12).
     */
    public function toggleActive(Application $application): RedirectResponse
    {
        $application->update(['is_active' => ! $application->is_active]);

        return back()->with('success', $application->is_active ? 'Aplikasi diaktifkan.' : 'Aplikasi dinonaktifkan.');
    }
}

ClientController

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\StoreClientRequest;
use App\Models\Client;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;

class ClientController extends Controller
{
    public function index(): View
    {
        return view('admin.clients.index', [
            'clients' => Client::query()->orderBy('name')->get(),
        ]);
    }

    public function store(StoreClientRequest $request): RedirectResponse
    {
        $data = $request->safe()->except('logo');

        if ($request->hasFile('logo')) {
            $data['logo'] = $request->file('logo')->store('client-logos', 'public');
        }

        Client::create($data + ['is_active' => true]);

        return back()->with('success', 'Instansi baru ditambahkan.');
    }

    public function update(StoreClientRequest $request, Client $client): RedirectResponse
    {
        $data = $request->safe()->except('logo');

        if ($request->hasFile('logo')) {
            $data['logo'] = $request->file('logo')->store('client-logos', 'public');
        }

        $client->update($data);

        return back()->with('success', 'Data instansi diperbarui.');
    }

    /**
     * Deactivate instead of delete: past complaints tied to this client must
     * stay intact for the monthly recap (§14), so we just hide it from the
     * public dropdown (§11).
     */
    public function toggleActive(Client $client): RedirectResponse
    {
        $client->update(['is_active' => ! $client->is_active]);

        return back()->with('success', $client->is_active ? 'Instansi diaktifkan.' : 'Instansi dinonaktifkan.');
    }
}

ComplaintController

<?php

namespace App\Http\Controllers\Admin;

use App\Actions\Complaint\CloseComplaintAction;
use App\Http\Controllers\Controller;
use App\Http\Requests\CloseComplaintRequest;
use App\Models\Complaint;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class ComplaintController extends Controller
{
    public function index(Request $request): View
    {
        $complaints = Complaint::query()
            ->with(['client', 'assignedTechnician'])
            ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
            ->latest('report_date')
            ->paginate(20)
            ->withQueryString();

        return view('admin.complaints.index', [
            'complaints' => $complaints,
            'statusFilter' => $request->string('status')->value(),
        ]);
    }

    public function show(Complaint $complaint): View
    {
        $complaint->load(['client', 'application.dedicatedTechnician', 'assignedTechnician', 'responses.technician', 'activities.causer']);

        return view('admin.complaints.show', ['complaint' => $complaint]);
    }

    /**
     * Admin sets the invoice price for a resolved ticket and closes it.
     * There is no manual "assign to technician" step anymore — technicians
     * claim open tickets themselves from their own dashboard (§4).
     */
    public function close(CloseComplaintRequest $request, Complaint $complaint, CloseComplaintAction $action): RedirectResponse
    {
        $action->handle($complaint, $request->user(), $request->validated('price'));

        return back()->with('success', 'Tagihan disimpan, tiket ditutup.');
    }
}

DashboardController

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Complaint;
use Illuminate\View\View;

class DashboardController extends Controller
{
    public function index(): View
    {
        $statusCounts = Complaint::query()
            ->selectRaw('status, count(*) as total')
            ->groupBy('status')
            ->pluck('total', 'status');

        $overdue = Complaint::query()
            ->whereIn('status', ['open', 'in_progress'])
            ->where(function ($query) {
                $query->where(fn ($q) => $q->where('priority', 'urgent')->where('report_date', '<=', now()->subHours(4)))
                    ->orWhere(fn ($q) => $q->where('priority', 'normal')->where('report_date', '<=', now()->subHours(48)));
            })
            ->count();

        return view('admin.dashboard', [
            'statusCounts' => $statusCounts,
            'overdueCount' => $overdue,
            'recentComplaints' => Complaint::query()->with('client')->latest('report_date')->take(10)->get(),
            // "open" always means unclaimed now — technicians self-claim, admin no longer pushes assignments (§4).
            'unclaimedComplaints' => Complaint::query()->where('status', 'open')->count(),
        ]);
    }
}

RecapController

<?php

namespace App\Http\Controllers\Admin;

use App\Actions\Recap\GenerateVerificationQrCode;
use App\Actions\Recap\StoreRecapReport;
use App\Http\Controllers\Controller;
use App\Models\Client;
use App\Models\Complaint;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;

class RecapController extends Controller
{
    public function index(): View
    {
        return view('admin.recap.index', [
            'clients' => Client::query()->orderBy('name')->get(),
        ]);
    }

    /**
     * Monthly recap per client. Price is set by admin per ticket when it's
     * closed (§14/§16) — only closed tickets have a price and count toward
     * the billable total; resolved-but-not-yet-invoiced tickets are shown
     * separately as still pending.
     */
    public function show(
        Request $request,
        GenerateVerificationQrCode $qrCode,
        StoreRecapReport $storeRecapReport,
    ): Response {
        $request->validate([
            'client_id' => ['required', 'uuid', 'exists:client,id'],
            'month' => ['required', 'integer', 'between:1,12'],
            'year' => ['required', 'integer', 'min:2020'],
        ]);

        $client = Client::findOrFail($request->string('client_id'));
        $month = $request->integer('month');
        $year = $request->integer('year');

        $complaints = Complaint::query()
            ->where('client_id', $client->id)
            ->whereYear('report_date', $year)
            ->whereMonth('report_date', $month)
            ->with('assignedTechnician')
            ->orderBy('report_date')
            ->get();

        $billable = $complaints->where('status', 'closed');
        $report = $storeRecapReport->handle($client, $month, $year, $complaints);

        $pdf = Pdf::loadView('admin.recap.pdf', [
            'client' => $client,
            'complaints' => $complaints,
            'billableTotal' => $billable->sum('price'),
            'billableCount' => $billable->count(),
            'month' => $month,
            'year' => $year,
            'report' => $report,
            'verificationQrCode' => $qrCode->handle(route('recap.verify', $report->token)),
        ])->setPaper('a4');

        $filename = "rekap-{$client->name}-{$month}-{$year}.pdf";

        return $pdf->stream($filename);
    }
}

SettingController

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\UpdateConfigRequest;
use App\Models\Config;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;

class SettingController extends Controller
{
    /**
     * Identitas perusahaan/instansi pengelola helpdesk ini — dipakai di
     * letterhead login, footer form pengaduan, dan header cetak rekap.
     * Selalu tepat satu baris (singleton), tidak ada create/delete.
     */
    public function edit(): View
    {
        return view('admin.settings.edit', [
            'config' => Config::query()->firstOrNew(),
        ]);
    }

    public function update(UpdateConfigRequest $request): RedirectResponse
    {
        $config = Config::query()->first();
        $data = $request->safe()->except('company_logo');

        if ($request->hasFile('company_logo')) {
            $data['company_logo'] = $request->file('company_logo')->store('config-logo', 'public');
        }

        if ($config) {
            $config->update($data);
        } else {
            Config::create($data);
        }

        return back()->with('success', 'Pengaturan berhasil disimpan.');
    }
}

TechnicianController

<?php

namespace App\Http\Controllers\Admin;

use App\Actions\User\CreateStaffAccountAction;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreStaffRequest;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;

class TechnicianController extends Controller
{
    public function index(): View
    {
        return view('admin.technicians.index', [
            'technicians' => User::query()->where('role', 'technician')->orderBy('name')->get(),
        ]);
    }

    public function store(StoreStaffRequest $request, CreateStaffAccountAction $action): RedirectResponse
    {
        $action->handle($request->validated(), 'technician');

        return back()->with('success', 'Akun teknisi dibuat. Email berisi password login sudah dikirim.');
    }

    public function update(StoreStaffRequest $request, User $technician): RedirectResponse
    {
        $technician->update($request->safe()->only(['name', 'username', 'email']));

        return back()->with('success', 'Data teknisi diperbarui.');
    }

    /**
     * Toggle active/inactive instead of a hard delete, so response/activity
     * history stays intact (see docs/ALUR_APLIKASI.md §12).
     */
    public function toggleActive(User $technician): RedirectResponse
    {
        $technician->update(['is_active' => ! $technician->is_active]);

        return back()->with('success', $technician->is_active ? 'Akun teknisi diaktifkan.' : 'Akun teknisi dinonaktifkan.');
    }
}

Komentar

Belum ada komentar.