Admin Panel
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-09-29 14:57:37 +07:00
parent 04fbddba13
commit 8c6dad2a31
94 changed files with 4864 additions and 1151 deletions
+12
View File
@@ -2,6 +2,7 @@
namespace Database\Factories;
use App\Enums\UserRole;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
@@ -27,6 +28,7 @@ class UserFactory extends Factory
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'role' => UserRole::Editor,
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
@@ -36,6 +38,16 @@ class UserFactory extends Factory
];
}
/**
* Indicate that the user is an admin (can also manage users).
*/
public function admin(): static
{
return $this->state(fn (array $attributes) => [
'role' => UserRole::Admin,
]);
}
/**
* Indicate that the model's email address should be unverified.
*/
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('editor')->after('email');
});
// Accounts created before roles existed were made with `admin:create`.
DB::table('users')->update(['role' => 'admin']);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
};
@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('consultations', function (Blueprint $table) {
$table->id();
// Which site form the request came from: home, services or contact.
$table->string('source');
$table->string('name');
$table->string('company')->nullable();
$table->string('job_title')->nullable();
$table->string('email');
$table->string('phone');
$table->string('industry')->nullable();
$table->string('service')->nullable();
$table->string('timeline')->nullable();
$table->text('details')->nullable();
$table->boolean('nda_agreed')->default(false);
$table->string('locale', 2)->default('id');
$table->string('status')->default('new')->index();
$table->text('admin_notes')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('consultations');
}
};
@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('projects', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('client');
$table->unsignedSmallInteger('year')->index();
$table->string('sector')->index();
$table->string('program')->nullable();
$table->string('scope')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('projects');
}
};
@@ -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.
*/
public function up(): void
{
Schema::create('offices', function (Blueprint $table) {
$table->id();
$table->string('name');
// Material Symbols icon name shown next to the office.
$table->string('icon')->default('apartment');
$table->string('building');
$table->string('street');
$table->string('area');
$table->string('city');
// Short location used in chips, e.g. "Bintaro, Jakarta Selatan".
$table->string('district');
$table->string('phone');
$table->string('maps_url')->nullable();
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('offices');
}
};
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('testimonials', function (Blueprint $table) {
$table->id();
// Text shown to visitors is stored in both site languages.
$table->text('quote_id');
$table->text('quote_en');
$table->string('role_id');
$table->string('role_en');
$table->string('organization_id');
$table->string('organization_en');
$table->boolean('is_published')->default(true);
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('testimonials');
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('site_settings', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->json('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('site_settings');
}
};
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace Database\Seeders;
use App\Enums\UserRole;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
/**
* Creates the first admin panel account from the ADMIN_* settings in .env.
* An existing account is left untouched, so re-seeding never resets a
* password that was changed from the panel.
*/
class AdminUserSeeder extends Seeder
{
public function run(): void
{
$email = config('app.admin.email');
if (User::query()->where('email', $email)->exists()) {
$this->command?->info("Admin {$email} sudah ada, dilewati.");
return;
}
$password = config('app.admin.password') ?: Str::password(16, symbols: false);
$user = User::query()->create([
'name' => config('app.admin.name'),
'email' => $email,
'role' => UserRole::Admin,
'password' => $password,
]);
$user->forceFill(['email_verified_at' => now()])->save();
if (config('app.admin.password')) {
$this->command?->info("Admin {$email} dibuat dengan ADMIN_PASSWORD dari .env.");
} else {
$this->command?->warn("Admin {$email} dibuat. Password (simpan sekarang, hanya ditampilkan sekali): {$password}");
}
}
}
+6 -6
View File
@@ -2,7 +2,6 @@
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
@@ -12,14 +11,15 @@ class DatabaseSeeder extends Seeder
/**
* Seed the application's database.
*
* The first admin comes from ADMIN_* in .env; more accounts can be
* added from the admin panel or with `php artisan admin:create`.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
$this->call([
AdminUserSeeder::class,
SiteContentSeeder::class,
]);
}
}
+136
View File
@@ -0,0 +1,136 @@
<?php
namespace Database\Seeders;
use App\Models\Office;
use App\Models\Project;
use App\Models\SiteSetting;
use App\Support\SiteInfo;
use Illuminate\Database\Seeder;
/**
* Initial website content: the project portfolio, office locations and
* contact details. Safe to re-run: each part is only seeded when empty.
*/
class SiteContentSeeder extends Seeder
{
public function run(): void
{
if (Project::query()->doesntExist()) {
foreach ($this->projects() as $project) {
Project::query()->create($project);
}
}
if (Office::query()->doesntExist()) {
foreach ($this->offices() as $index => $office) {
Office::query()->create([...$office, 'sort_order' => $index]);
}
}
if (SiteSetting::query()->where('key', SiteInfo::CONTACT_KEY)->doesntExist()) {
SiteSetting::put(SiteInfo::CONTACT_KEY, SiteInfo::DEFAULT_CONTACT);
}
}
/**
* @return list<array<string, mixed>>
*/
private function offices(): array
{
return [
[
'name' => 'PAJ Software Engineering Center',
'icon' => 'code_blocks',
'building' => 'Anindhaloka',
'street' => 'Jl. YRS No.20, RW.1',
'area' => 'Bintaro, Kec. Pesanggrahan',
'city' => 'Kota Jakarta Selatan 12330',
'district' => 'Bintaro, Jakarta Selatan',
'phone' => '(021) 7266-3353',
'maps_url' => 'https://maps.google.com/?q=Jl.+YRS+No.20+Bintaro+Pesanggrahan+Jakarta+Selatan',
],
[
'name' => 'PAJ Business Development Center',
'icon' => 'apartment',
'building' => 'Plaza Simatupang Lt. 6 Unit 3',
'street' => 'Jl. TB. Simatupang Kav. IS No. 01',
'area' => 'Pondok Pinang, Kebayoran Lama',
'city' => 'Jakarta Selatan 12310',
'district' => 'Plaza Simatupang, Jakarta Selatan',
'phone' => '+62 21 5789 5927',
'maps_url' => 'https://maps.google.com/?q=Plaza+Simatupang+Jakarta',
],
];
}
/**
* @return list<array<string, mixed>>
*/
private function projects(): array
{
return [
['title' => 'Penyusunan dan Pembuatan SOP Direktorat Data dan Informasi', 'client' => 'Kementerian Investasi / Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2025, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Perencanaan Peta Jalan Direktorat Data dan Informasi TA 2024', 'client' => 'Kementerian Investasi / Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2024, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Modul Pelayanan Metrologi Legal Submodul Verifikasi (Sistem Informasi Ketertelusuran Standar (SIKS))', 'client' => 'Kementerian Perdagangan', 'year' => 2024, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Penyediaan Data Harga dan Pasokan Cabe dan Bawang di Pasar Induk Wilayah Barat', 'client' => 'Kementerian Perdagangan', 'year' => 2024, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pemeliharaan Sistem Pelaporan Harga (SP2KP), Pusat Data dan Sistem Informasi', 'client' => 'Kementerian Perdagangan', 'year' => 2023, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Penyediaan Data Harga dan Pasokan Cabe dan Bawang di Pasar Induk Wilayah Barat', 'client' => 'Kementerian Perdagangan', 'year' => 2023, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pembangunan Aplikasi Sertifikasi Prasarana Perkeretaapian Dengan Memanfaatkan Teknologi Informasi', 'client' => 'Kementerian Perhubungan', 'year' => 2022, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pengembangan Aplikasi LDH', 'client' => 'Badan Informasi Geospasial (BIG)', 'year' => 2022, 'sector' => 'government', 'program' => 'erp', 'scope' => 'OpenERP Community: manajemen dokumen dan custom development'],
['title' => 'Perawatan Sistem Pelaporan Harga', 'client' => 'Kementerian Perdagangan', 'year' => 2022, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pengembangan Aplikasi Sistem Informasi Ketertelusuran Standar', 'client' => 'Kementerian Perdagangan', 'year' => 2021, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pengembangan Modul Master Data Aplikasi SIREBEKA (Sistem Informasi Remunerasi Berbasis Kinerja)', 'client' => 'Kementerian Perhubungan', 'year' => 2021, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pengelolaan Sistem Pelaporan Harga', 'client' => 'Kementerian Perdagangan', 'year' => 2020, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pengadaan Metrologi Aplikasi Mobile: Pendataan dan Pemetaan Kondisi UTTP dan BDKT dalam Kerangka Pengawasan Kemetrologian', 'client' => 'Kementerian Perdagangan', 'year' => 2020, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Penyediaan Data Harga dan Pasokan Cabe dan Bawang di Pasar Induk Wilayah Barat', 'client' => 'Kementerian Perdagangan', 'year' => 2020, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Penyediaan Data Harga dan Pasokan Cabe dan Bawang di Pasar Induk Wilayah Timur', 'client' => 'Kementerian Perdagangan', 'year' => 2020, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Perawatan Sistem Pelaporan Harga', 'client' => 'Kementerian Perdagangan', 'year' => 2020, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Perancangan Antar Muka Sistem Informasi Ketertelusuran Standar', 'client' => 'Kementerian Perdagangan', 'year' => 2019, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pengembangan dan Pemeliharaan Portal NSWI TA 2019', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2019, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Pengembangan Aplikasi Website Intranet', 'client' => 'Kementerian Perdagangan', 'year' => 2018, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pemantauan Harga Bapok 34 Ibukota Provinsi pada Sabtu atau Minggu, Libur Nasional dan HBKN, Pasar Kab/Kota, Daerah TTT dan Pasar Ritel Modern Tahun 2018', 'client' => 'Kementerian Perdagangan', 'year' => 2018, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pengembangan Business Intelligence (BI) TA 2018', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2018, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Pengembangan Dashboard Produk Wisata MICE', 'client' => 'Kementerian Pariwisata', 'year' => 2017, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pembuatan Sistem Informasi Manajemen Harga dan Pasokan', 'client' => 'Kementerian Perdagangan', 'year' => 2017, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Penerapan Model Pembangunan dan Revitalisasi Pasar Rakyat Dengan Pendekatan Teknologi Informasi Berbasis Mobile', 'client' => 'Kementerian Perdagangan', 'year' => 2017, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Penyusunan Pemutakhiran (Updating) Cetak Biru Pengembangan Sistem Pelayanan Informasi dan Perizinan', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2017, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Pemantauan Stok dan Pasokan Barang Kebutuhan Pokok 100 Titik di Pasar Rakyat (Tradisional), Pasar Induk dan Sentra Produksi', 'client' => 'Kementerian Perdagangan', 'year' => 2017, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Verifikasi Pelaporan Harga Barang Pokok pada SP2KP', 'client' => 'Kementerian Perdagangan', 'year' => 2016, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pengembangan Program Aplikasi Harga Kabupaten Kota', 'client' => 'Kementerian Perdagangan', 'year' => 2016, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Updating Program Penilaian Pasar', 'client' => 'Kementerian Perdagangan', 'year' => 2016, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pengembangan Sistem Aplikasi Jaringan Informasi Dokumentasi Hukum', 'client' => 'Badan Informasi Geospasial (BIG)', 'year' => 2016, 'sector' => 'government', 'program' => 'erp', 'scope' => 'OpenERP Community: manajemen dokumen dan custom development'],
['title' => 'Verifikasi Pelaporan Harga Barang Pokok pada Sistem Pemantauan Pasar Kebutuhan Pokok (SP2KP)', 'client' => 'Kementerian Perdagangan', 'year' => 2016, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pekerjaan Pengembangan Program Aplikasi Harga Kabupaten Kota', 'client' => 'Kementerian Perdagangan', 'year' => 2016, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pembuatan Software pada Kegiatan Penguatan Regional Climate Change Center', 'client' => 'Badan Meteorologi Klimatologi dan Geofisika (BMKG)', 'year' => 2015, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Kajian Tata Kelola Teknologi Informasi BKPM TA 2014', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2014, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Sistem Informasi Monitoring Updating Harga dan Pasokan Bawang Merah', 'client' => 'Kementerian Perdagangan', 'year' => 2014, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pemetaan Indikasi dan Variabel Penentu Kenaikan Harga Bahan Pokok (Komoditas Cabe Merah, Bawang Merah dan Beras)', 'client' => 'Kementerian Perdagangan', 'year' => 2014, 'sector' => 'government', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pembangunan Sistem Informasi Pelayanan Dokumen Hukum', 'client' => 'Badan Informasi Geospasial (BIG)', 'year' => 2014, 'sector' => 'government', 'program' => 'erp', 'scope' => 'OpenERP Community: manajemen dokumen dan custom development'],
['title' => 'Pendampingan Helpdesk Implementasi Sistem Pelayanan Informasi dan Perizinan Investasi Secara Elektronik (SPIPISE)', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2013, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Pengadaan Jasa Konsultasi Pekerjaan Pembangunan Sub Sistem Bidang Ketahanan Lembaga Usaha Ekonomi', 'client' => 'Kementerian Perdagangan', 'year' => 2012, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pendampingan Helpdesk Implementasi Sistem Pelayanan Informasi dan Perizinan Investasi Secara Elektronik (SPIPISE)', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2012, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Pembangunan Aplikasi Jaringan Dokumentasi dan Informasi Hukum', 'client' => 'Badan Informasi Geospasial (BIG)', 'year' => 2012, 'sector' => 'government', 'program' => 'erp', 'scope' => 'OpenERP Community: manajemen dokumen dan custom development'],
['title' => 'Pengembangan Portal National Single Window for Investment (NSWI) untuk Mendukung SPIPISE BKPM', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2010, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Pembuatan Database Eksportir Importir, Perusahaan, Asosiasi dan Pelaku Industri Kreatif serta Lembaga Pendidikan Formal/Non Formal', 'client' => 'Departemen Perdagangan Republik Indonesia', 'year' => 2009, 'sector' => 'government', 'program' => null, 'scope' => null],
['title' => 'Pembangunan Sistem Informasi Tracking Promosi Investasi Tahun Anggaran 2006', 'client' => 'Badan Koordinasi Penanaman Modal (BKPM)', 'year' => 2006, 'sector' => 'government', 'program' => 'investment', 'scope' => null],
['title' => 'Implementasi Odoo ERP', 'client' => 'Marawah Beach Club', 'year' => 2022, 'sector' => 'private', 'program' => 'erp', 'scope' => 'Odoo Enterprise: POS, event, pembelian, penjualan, website, dan custom development'],
['title' => 'Enhancement Manajemen Sistem', 'client' => 'PT Cipta Kridatama', 'year' => 2022, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Implementasi OpenERP Manufacturing System', 'client' => 'CV Mujur Jaya Abadi', 'year' => 2015, 'sector' => 'private', 'program' => 'erp', 'scope' => 'OpenERP Community: akunting, manufaktur, pembelian, penjualan, inventory, dan custom development'],
['title' => 'Pengembangan Reswara Integrated Management System', 'client' => 'PT Reswara Minergi Hartama', 'year' => 2013, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Kegiatan Pengembangan dan Operasionalisasi Sistem Pemantauan Pasar Kebutuhan Pokok (SP2KP)', 'client' => 'PT LAPI ITB (Bapokstra)', 'year' => 2013, 'sector' => 'private', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pengembangan Aplikasi GIS Pelaporan Online Harga', 'client' => 'PT DKN', 'year' => 2011, 'sector' => 'private', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pengembangan Portal Ekonomi Kreatif Indonesia (www.indonesiakreatif.net)', 'client' => 'CV Multi Sarana Putra', 'year' => 2010, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Pengembangan Cetak Biru Teknologi Informasi Pusat Pengendalian Online Harga dan Stok', 'client' => 'PT Pranala Nitisala', 'year' => 2010, 'sector' => 'private', 'program' => 'price-monitoring', 'scope' => null],
['title' => 'Pembuatan Sistem Informasi Monitoring Penerimaan dan Pengeluaran BBM di Setiap Fasilitas Rantai Pasok BBM', 'client' => 'KSO LAPI – Pranala', 'year' => 2008, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Validasi dan Koreksi Data Akunting', 'client' => 'PT Lautan Berlian Utama Motor', 'year' => 2008, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Pengembangan, Pemutakhiran dan Pemeliharaan Website Tahun Anggaran 2007', 'client' => 'CV Multi Sarana Putra', 'year' => 2007, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Pembuatan Compact Disc (CD) Profil Wilayah, Sektor dan Komoditi', 'client' => 'PT Qualita Prima Pariwara', 'year' => 2007, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Pengembangan Aplikasi Titis Sampurna Accounting Information System', 'client' => 'PT Titis Sampurna', 'year' => 2006, 'sector' => 'private', 'program' => 'erp', 'scope' => null],
['title' => 'Jasa Konsultasi Pengembangan Sistem Informasi Eksekutif Keuangan', 'client' => 'CV Multi Sarana Putra', 'year' => 2006, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Desain Sistem Informasi Museum Bank Indonesia', 'client' => 'PT Global Media Inline', 'year' => 2006, 'sector' => 'private', 'program' => null, 'scope' => null],
['title' => 'Pembangunan Sistem Aplikasi', 'client' => 'PT Sucofindo (Persero)', 'year' => 2015, 'sector' => 'soe', 'program' => null, 'scope' => null],
['title' => 'Jasa Pemeliharaan Sistem Merpati Operation System Infrastructure (MOSI), Mobile Flight Log (MFL), Schedule Messaging System (SMS)', 'client' => 'PT Merpati Nusantara Airlines', 'year' => 2006, 'sector' => 'soe', 'program' => null, 'scope' => null],
];
}
}