Skip to content
Open
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
125 changes: 58 additions & 67 deletions app/Livewire/DashboardTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace App\Livewire;

use App\Models\Postcard;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use Livewire\WithPagination;
Expand All @@ -29,7 +28,7 @@ class DashboardTable extends Component

public $filter_status = '';

public $sort = 'tanggal_kirim'; // default
public $sort = 'tanggal_kirim';

public $perPage = 20;

Expand Down Expand Up @@ -60,87 +59,79 @@ public function render()
$query->whereBetween('tanggal_terima', [$this->start_terima, $this->end_terima]);
}

// SQL JOIN for Country Search
// Filters
if ($this->filter_negara) {
$searchTerm = $this->filter_negara;
$query->whereHas('country', function ($q) use ($searchTerm) {
$q->where('nama_indonesia', 'like', '%'.$searchTerm.'%')
->orWhere('nama_inggris', 'like', '%'.$searchTerm.'%');
});
}

if ($this->filter_kategori === 'postcrossing') {
$query->where('postcard_id', 'like', '%-%');
} elseif ($this->filter_kategori === 'swap') {
$query->where(function ($q) {
$q->where('postcard_id', 'not like', '%-%')
->orWhereNull('postcard_id')
->orWhere('postcard_id', '');
});
}

if ($this->filter_status === 'arrived') {
$query->whereNotNull('tanggal_terima')->where('tanggal_terima', '!=', '0000-00-00');
} elseif ($this->filter_status === 'travelling') {
$query->where(function ($q) {
$q->whereNull('tanggal_terima')->orWhere('tanggal_terima', '0000-00-00');
});
}

$allRecords = $query->get();

if ($this->filter_negara) {
$searchTerm = strtolower($this->filter_negara);
$allRecords = $allRecords->filter(function ($row) use ($searchTerm) {
// Country search already handled by SQL whereHas, but adding others here
return str_contains(strtolower((string) $row->contact?->nama_kontak), $searchTerm) ||
str_contains(strtolower((string) $row->contact?->alamat), $searchTerm) ||
str_contains(strtolower((string) $row->contact?->nomor_telepon), $searchTerm) ||
str_contains(strtolower((string) $row->postcard_id), $searchTerm) ||
str_contains(strtolower((string) $row->deskripsi_gambar), $searchTerm);
$searchTerm = '%'.$this->filter_negara.'%';
$query->where(function ($q) use ($searchTerm) {
$q->whereHas('country', function ($sq) use ($searchTerm) {
$sq->where('nama_indonesia', 'like', $searchTerm)
->orWhere('nama_inggris', 'like', $searchTerm);
})
->orWhereHas('contact', function ($sq) use ($searchTerm) {
$sq->where('nama_kontak', 'like', $searchTerm)
->orWhere('alamat', 'like', $searchTerm)
->orWhere('nomor_telepon', 'like', $searchTerm);
})
->orWhere('postcard_id', 'like', $searchTerm)
->orWhere('deskripsi_gambar', 'like', $searchTerm);
});
}

// Handle Sorting
if (str_starts_with($this->sort, 'jarak')) {
$allRecords->each(function ($row) use ($myLat, $myLng) {
$row->jarak_hitung = ($row->contact?->lat && $row->contact?->lng && is_numeric($row->contact->lat))
? $this->calculateDistance($myLat, $myLng, (float) $row->contact->lat, (float) $row->contact->lng)
: 0;
$allRows = $query->get()->each(function ($row) use ($myLat, $myLng) {
if ($row->contact && $row->contact->lat && $row->contact->lng) {
$row->jarak_hitung = $this->calculateDistance($myLat, $myLng, $row->contact->lat, $row->contact->lng);
} else {
$row->jarak_hitung = 0;
}
});

if ($this->sort === 'jarak_desc') {
$allRecords = $allRecords->sortByDesc('jarak_hitung');
$allRows = $allRows->sortByDesc('jarak_hitung');
} else {
$allRecords = $allRecords->sortBy('jarak_hitung');
$allRows = $allRows->sort(function ($a, $b) {
if ($a->jarak_hitung == 0 && $b->jarak_hitung == 0) {
return 0;
}
if ($a->jarak_hitung == 0) {
return 1;
}
if ($b->jarak_hitung == 0) {
return -1;
}

return $a->jarak_hitung <=> $b->jarak_hitung;
});
}
} elseif ($this->sort === 'tanggal_terima') {
$allRecords = $allRecords->sortByDesc('tanggal_kirim')
->sortByDesc('tanggal_terima');

$currentPage = \Illuminate\Pagination\Paginator::resolveCurrentPage();
$rows = new \Illuminate\Pagination\LengthAwarePaginator(
$allRows->forPage($currentPage, $this->perPage)->values(),
$allRows->count(),
$this->perPage,
$currentPage,
['path' => request()->url(), 'query' => request()->query()]
);
} else {
// Default: tanggal_kirim DESC
$allRecords = $allRecords->sortByDesc('tanggal_kirim');
}
if ($this->sort === 'tanggal_terima') {
$query->orderByDesc('tanggal_terima')->orderByDesc('tanggal_kirim');
} else {
$query->orderByDesc('tanggal_kirim')->orderByDesc('id');
}

$rows = $query->paginate($this->perPage);

$page = $this->getPage();
$rows = new LengthAwarePaginator(
$allRecords->forPage($page, $this->perPage),
$allRecords->count(),
$this->perPage,
$page,
['path' => \Illuminate\Pagination\Paginator::resolveCurrentPath()]
);

// Ensure distance is calculated for result page if not done in sort step
if (! str_starts_with($this->sort, 'jarak')) {
$rows->getCollection()->each(function ($row) use ($myLat, $myLng) {
$row->jarak_hitung = ($row->contact?->lat && $row->contact?->lng && is_numeric($row->contact->lat))
? $this->calculateDistance($myLat, $myLng, (float) $row->contact->lat, (float) $row->contact->lng)
: 0;
if ($row->contact && $row->contact->lat && $row->contact->lng) {
$row->jarak_hitung = $this->calculateDistance($myLat, $myLng, $row->contact->lat, $row->contact->lng);
} else {
$row->jarak_hitung = 0;
}
});
}

// Grand Total based on PHP-filtered records
$totalCost = $allRecords->sum('biaya_prangko');
$totalCost = Postcard::where('user_id', $user_id)->where('type', $this->type)->sum('biaya_prangko');

return view('livewire.dashboard-table', [
'rows' => $rows,
Expand Down
5 changes: 1 addition & 4 deletions app/Livewire/EditPostcard.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,21 @@ class EditPostcard extends Component

public $deskripsi_gambar;

// Currency fields (for received)
public $nilai_asal;

public $mata_uang = 'IDR';

public $kurs_idr = 1;

// Images
public $currentFotoDepan;

public $currentFotoBelakang;

// JS Scanner will fill these Base64 strings
public $newFotoDepanBase64;

public $newFotoBelakangBase64;

public $newStampsBase64 = []; // Array of Base64 strings
public $newStampsBase64 = [];

public $existingStamps = [];

Expand Down
9 changes: 8 additions & 1 deletion app/Livewire/RegisterPostcard.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class RegisterPostcard extends Component

public $deskripsi_gambar;

// Currency
public $nilai_asal;

public $mata_uang = 'IDR';
Expand Down Expand Up @@ -114,6 +113,14 @@ protected function saveImage($base64Data, $prefix)

public function save(GeocodingService $geoService)
{
$this->validate([
'negara' => 'required',
'alamat' => 'required',
'tanggal_kirim' => 'required|date',
'img_d_data' => 'nullable', // Optional but handled
'img_b_data' => 'nullable',
]);

$coords = $geoService->getCoordinates($this->alamat, $this->negara);
$lat = $coords['lat'];
$lng = $coords['lng'];
Expand Down
40 changes: 20 additions & 20 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 18 additions & 10 deletions public/sw.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const CACHE_NAME = 'postcrossing-v4';
const CACHE_NAME = 'postcrossing-v7';
const ASSETS = [
'/logo-app.png',
'/logo.png',
'/vendor/bootstrap-icons/bootstrap-icons.css',
'/offline.html' // Optional: Create an offline page
'/offline.html'
];

self.addEventListener('install', (event) => {
Expand All @@ -28,7 +28,20 @@ self.addEventListener('activate', (event) => {
});

self.addEventListener('fetch', (event) => {
// Navigation requests (HTML pages) -> Network Only (Audit: User reported stale data)
const url = new URL(event.request.url);

// Cache-first for static assets
const isAsset = ASSETS.some(asset => url.pathname === asset);
if (isAsset) {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
return;
}

// Network-only with offline fallback for navigation
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(() => {
Expand All @@ -38,10 +51,5 @@ self.addEventListener('fetch', (event) => {
return;
}

// Static Assets -> Cache First, then Network
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
// Bypass for dynamic requests (Livewire/API)
});
5 changes: 4 additions & 1 deletion resources/views/components/layouts/app.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#2c3e50">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

<!-- Local Assets Only -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
Expand Down Expand Up @@ -238,7 +241,7 @@
const installBtn = document.getElementById('pwaInstallBtn');

if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
navigator.serviceWorker.register('/sw.js?v=7');
}

window.addEventListener('beforeinstallprompt', (e) => {
Expand Down
Loading
Loading