Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion app/Http/Controllers/LaporanDesaAktifController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,102 @@ public function index(Request $request)
if ($request->ajax()) {
$_30HariLalu = Carbon::now()->subDays(30);

return DataTables::of(Desa::fillter($fillters)->withCount(['akses' => static fn ($q) => $q->where('created_at', '>=', $_30HariLalu)])->where('updated_at', '>=', $_30HariLalu))
$query = Desa::query()
->select([
'desa.id',
'desa.nama_desa',
'desa.jml_surat_tte',
'desa.jml_mandiri',
'desa.jml_artikel',
'desa.jml_dokumen',
'desa.kode_provinsi',
'desa.kode_kabupaten',
'desa.kode_kecamatan',
'desa.versi_lokal',
'desa.versi_hosting',
'desa.tgl_akses_lokal',
'desa.tgl_akses_hosting',
'desa.modul_tte',
'desa.updated_at',
])
->selectRaw(
'(SELECT COUNT(*) FROM akses WHERE akses.desa_id = desa.id AND akses.created_at >= ?) as akses_count',
[$_30HariLalu]
)
->where('desa.updated_at', '>=', $_30HariLalu);

$this->applyFilters($query, $fillters);

return DataTables::of($query)
->addIndexColumn()
->make(true);
}

return view('laporan.desa_aktif', compact('fillters'));
}

/**
* Apply filters directly without calling scopeFillter (which overrides SELECT with *).
*/
private function applyFilters($query, array $fillters): void
{
$fillters = array_merge([
'kode_provinsi' => null,
'kode_kabupaten' => null,
'kode_kecamatan' => null,
'akses' => null,
'status' => null,
'versi_lokal' => null,
'versi_hosting' => null,
'tte' => null,
], $fillters);

$query
->when($fillters['kode_provinsi'] ?? false, function ($q, $kode_provinsi) {
$q->where('desa.kode_provinsi', $kode_provinsi);
})
->when($fillters['kode_kabupaten'] ?? false, function ($q, $kode_kabupaten) {
$q->where('desa.kode_kabupaten', $kode_kabupaten);
})
->when($fillters['kode_kecamatan'] ?? false, function ($q, $kode_kecamatan) {
$q->where('desa.kode_kecamatan', $kode_kecamatan);
})
->when($fillters['status'] == 1, function ($q) {
$q->whereNotNull('desa.versi_hosting')->whereNull('desa.versi_lokal');
})
->when($fillters['status'] == 2, function ($q) {
$q->whereNotNull('desa.versi_lokal')->whereNull('desa.versi_hosting');
})
->when($fillters['status'] == 3, function ($q) {
$q->where(function ($sub) {
$version = lastrelease_opensid();
$sub->where('desa.versi_hosting', 'LIKE', $version . '-premium%')
->orWhere('desa.versi_lokal', 'LIKE', $version . '-premium%');
});
})
->when($fillters['akses'] == 1, function ($q) {
$q->whereRaw('timestampdiff(month, greatest(coalesce(desa.tgl_akses_lokal, 0), coalesce(desa.tgl_akses_hosting, 0)), now()) > 1');
})
->when($fillters['akses'] == 2, function ($q) {
$q->whereRaw('timestampdiff(month, greatest(coalesce(desa.tgl_akses_lokal, 0), coalesce(desa.tgl_akses_hosting, 0)), now()) <= 1');
})
->when($fillters['akses'] == 3, function ($q) {
$q->whereRaw('timestampdiff(month, greatest(coalesce(desa.tgl_akses_lokal, 0), coalesce(desa.tgl_akses_hosting, 0)), now()) > 3');
})
->when($fillters['akses'] == 4, function ($q) {
$q->whereRaw('greatest(coalesce(desa.tgl_akses_lokal, 0), coalesce(desa.tgl_akses_hosting, 0)) >= now() - interval 7 day');
})
->when($fillters['akses'] == 5, function ($q) {
$q->whereRaw("desa.versi_lokal <> '' and desa.versi_hosting is null and coalesce(desa.tgl_akses_lokal, 0) >= now() - interval 7 day");
})
->when($fillters['versi_lokal'], function ($q, $versi) {
$q->where('desa.versi_lokal', $versi);
})
->when($fillters['versi_hosting'], function ($q, $versi) {
$q->where('desa.versi_hosting', $versi);
})
->when(in_array($fillters['tte'], ['1', '0']), function ($q) use ($fillters) {
$q->where('desa.modul_tte', $fillters['tte']);
});
}
}
25 changes: 25 additions & 0 deletions database/factories/AksesFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Database\Factories;

use App\Models\Akses;
use App\Models\Desa;
use Illuminate\Database\Eloquent\Factories\Factory;

class AksesFactory extends Factory
{
protected $model = Akses::class;

public function definition()
{
return [
'desa_id' => Desa::factory(),
'url_referrer' => $this->faker->url(),
'request_uri' => '/' . $this->faker->word(),
'client_ip' => $this->faker->ipv4(),
'external_ip' => $this->faker->ipv4(),
'opensid_version' => $this->faker->randomElement(['2507.0.0', '2403.0.0']),
'tgl' => $this->faker->dateTimeBetween('-30 days', 'now'),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('akses', function (Blueprint $table) {
$table->index(['desa_id', 'created_at'], 'idx_akses_desa_created');
});

Schema::table('desa', function (Blueprint $table) {
$table->index('updated_at', 'idx_desa_updated_at');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('akses', function (Blueprint $table) {
$table->dropIndex('idx_akses_desa_created');
});

Schema::table('desa', function (Blueprint $table) {
$table->dropIndex('idx_desa_updated_at');
});
}
};
132 changes: 132 additions & 0 deletions tests/Feature/LaporanDesaAktifTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

namespace Tests\Feature;

use App\Models\Akses;
use App\Models\Desa;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;

class LaporanDesaAktifTest extends TestCase
{
use DatabaseTransactions;

protected $user;

protected function setUp(): void
{
parent::setUp();

$this->user = User::factory()->create([
'email_verified_at' => now(),
]);
}

public function test_laporan_desa_aktif_page_loads()
{
$response = $this->actingAs($this->user)
->get('/laporan/desa-aktif');

$response->assertStatus(200);
$response->assertViewIs('laporan.desa_aktif');
}

public function test_laporan_desa_aktif_ajax_returns_datatable_json()
{
$response = $this->actingAs($this->user)
->get('/laporan/desa-aktif?length=999', [
'X-Requested-With' => 'XMLHttpRequest',
]);

$response->assertStatus(200);
$response->assertJsonStructure([
'draw',
'recordsTotal',
'recordsFiltered',
'data',
]);
}

public function test_active_desa_is_included_in_response()
{
// Create active desa (within 30 days)
$desa = Desa::factory()->create([
'nama_desa' => 'DesaTestAktif_' . uniqid(),
'updated_at' => now()->subDays(5),
]);

$response = $this->actingAs($this->user)
->get('/laporan/desa-aktif?length=999', [
'X-Requested-With' => 'XMLHttpRequest',
]);

$response->assertStatus(200);
$data = $response->json('data');

$names = collect($data)->pluck('nama_desa')->toArray();
$this->assertContains($desa->nama_desa, $names);
}

public function test_akses_count_is_present_in_response()
{
$desa = Desa::factory()->create([
'nama_desa' => 'DesaAksesCount_' . uniqid(),
'updated_at' => now(),
]);

// Create recent akses records (within 30 days)
Akses::factory()->count(3)->create([
'desa_id' => $desa->id,
'created_at' => now()->subDays(5),
]);

// Create old akses records (older than 30 days, should NOT be counted)
Akses::factory()->count(2)->create([
'desa_id' => $desa->id,
'created_at' => now()->subDays(45),
]);

$response = $this->actingAs($this->user)
->get('/laporan/desa-aktif?length=999', [
'X-Requested-With' => 'XMLHttpRequest',
]);

$response->assertStatus(200);
$data = $response->json('data');

$desaData = collect($data)->firstWhere('nama_desa', $desa->nama_desa);
$this->assertNotNull($desaData, 'Test desa should be in response');
$this->assertEquals(3, $desaData['akses_count'], 'Only recent akses should be counted');
}

public function test_filter_by_provinsi_works()
{
$uniqueSuffix = uniqid();

$desaJawa = Desa::factory()->create([
'nama_desa' => 'DesaJawa_' . $uniqueSuffix,
'kode_provinsi' => '99',
'updated_at' => now(),
]);

$desaSumatra = Desa::factory()->create([
'nama_desa' => 'DesaSumatra_' . $uniqueSuffix,
'kode_provinsi' => '98',
'updated_at' => now(),
]);

$response = $this->actingAs($this->user)
->get('/laporan/desa-aktif?kode_provinsi=99&length=999', [
'X-Requested-With' => 'XMLHttpRequest',
]);

$response->assertStatus(200);
$data = $response->json('data');

$names = collect($data)->pluck('nama_desa')->toArray();
$this->assertContains($desaJawa->nama_desa, $names);
$this->assertNotContains($desaSumatra->nama_desa, $names);
}
}
Loading