feat(stationmgmt): add CSV import and export buttons for station management
This commit is contained in:
35
src/app/Exports/StationExport.php
Normal file
35
src/app/Exports/StationExport.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
|
||||||
|
class StationExport implements FromCollection, WithHeadings
|
||||||
|
{
|
||||||
|
public function collection()
|
||||||
|
{
|
||||||
|
return DB::table('station')
|
||||||
|
->select('stationid', 'name', 'district', 'lng', 'lat', 'mainriverbasin', 'subriverbasin', 'rainfall', 'waterlevel', 'siren', 'cctv_link')
|
||||||
|
->orderBy('stationid')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Station ID',
|
||||||
|
'Name',
|
||||||
|
'District',
|
||||||
|
'Longitude',
|
||||||
|
'Latitude',
|
||||||
|
'Main River Basin',
|
||||||
|
'Sub River Basin',
|
||||||
|
'Rainfall',
|
||||||
|
'Water Level',
|
||||||
|
'Siren',
|
||||||
|
'CCTV Link',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,9 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
use App\Exports\StationExport;
|
||||||
|
use App\Imports\StationImport;
|
||||||
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
{
|
{
|
||||||
@@ -271,4 +274,33 @@ class AdminController extends Controller
|
|||||||
return redirect()->back()->with('success',__('toast.userdeleted'));
|
return redirect()->back()->with('success',__('toast.userdeleted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function Export Stations to CSV
|
||||||
|
public function exportStations()
|
||||||
|
{
|
||||||
|
return Excel::download(new StationExport, 'stations.csv', \Maatwebsite\Excel\Excel::CSV);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function Import Stations from CSV
|
||||||
|
public function importStations(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'csv_file' => 'required|file|mimes:csv,txt|max:10240',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Excel::import(new StationImport, $request->file('csv_file'));
|
||||||
|
return redirect()->route('stationmanagement')->with('success', __('toast.stationsimported'));
|
||||||
|
} catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
|
||||||
|
$failures = $e->failures();
|
||||||
|
$firstError = collect($failures)->first();
|
||||||
|
$msg = $firstError
|
||||||
|
? "Row {$firstError->row()}: {$firstError->errors()[0]}"
|
||||||
|
: __('toast.error');
|
||||||
|
return redirect()->route('stationmanagement')->with('error', $msg);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Station CSV import failed', ['error' => $e->getMessage()]);
|
||||||
|
return redirect()->route('stationmanagement')->with('error', __('toast.error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/app/Imports/StationImport.php
Normal file
37
src/app/Imports/StationImport.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Imports;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||||
|
|
||||||
|
class StationImport implements ToCollection, WithHeadingRow
|
||||||
|
{
|
||||||
|
public function collection(Collection $rows)
|
||||||
|
{
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$stationid = $row['station_id'] ?? $row['stationid'] ?? null;
|
||||||
|
if (!$stationid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('station')->updateOrInsert(
|
||||||
|
['stationid' => $stationid],
|
||||||
|
[
|
||||||
|
'name' => $row['name'] ?? '',
|
||||||
|
'district' => $row['district'] ?? '',
|
||||||
|
'lng' => is_numeric($row['longitude'] ?? null) ? $row['longitude'] : 0,
|
||||||
|
'lat' => is_numeric($row['latitude'] ?? null) ? $row['latitude'] : 0,
|
||||||
|
'mainriverbasin' => $row['main_river_basin'] ?? $row['mainriverbasin'] ?? '',
|
||||||
|
'subriverbasin' => $row['sub_river_basin'] ?? $row['subriverbasin'] ?? '',
|
||||||
|
'rainfall' => intval($row['rainfall'] ?? 0),
|
||||||
|
'waterlevel' => intval($row['water_level'] ?? $row['waterlevel'] ?? 0),
|
||||||
|
'siren' => intval($row['siren'] ?? 0),
|
||||||
|
'cctv_link' => $row['cctv_link'] ?? null,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,6 +94,9 @@
|
|||||||
'nohistorysiren' => 'Tiada Data Sejarah Siren',
|
'nohistorysiren' => 'Tiada Data Sejarah Siren',
|
||||||
'nocurrentsiren' => 'Tiada siren aktif sejak 7 hari lalu',
|
'nocurrentsiren' => 'Tiada siren aktif sejak 7 hari lalu',
|
||||||
'nodataavailable' => 'Tiada Data Tersedia',
|
'nodataavailable' => 'Tiada Data Tersedia',
|
||||||
|
'dailyrainfall' => 'Hujan Harian',
|
||||||
|
'importcsv' => 'Import CSV',
|
||||||
|
'exportcsv' => 'Eksport CSV',
|
||||||
|
|
||||||
//Form
|
//Form
|
||||||
'selectstation' => 'Pilih Stesen',
|
'selectstation' => 'Pilih Stesen',
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
'passwordupdated' => 'Kata laluan berjaya dikemaskini',
|
'passwordupdated' => 'Kata laluan berjaya dikemaskini',
|
||||||
'stationdeleted' => 'Stesen berjaya dipadam',
|
'stationdeleted' => 'Stesen berjaya dipadam',
|
||||||
'userdeleted' => 'Pengguna berjaya dipadam',
|
'userdeleted' => 'Pengguna berjaya dipadam',
|
||||||
|
'linksupdated' => 'Pautan CCTV berjaya dikemaskini',
|
||||||
|
'stationsimported' => 'Stesen berjaya diimport',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,9 @@
|
|||||||
'nohistorysiren' => 'No Siren History Data',
|
'nohistorysiren' => 'No Siren History Data',
|
||||||
'nocurrentsiren' => 'No siren triggered for the past 7 days',
|
'nocurrentsiren' => 'No siren triggered for the past 7 days',
|
||||||
'nodataavailable' => 'No Data Avaiable',
|
'nodataavailable' => 'No Data Avaiable',
|
||||||
|
'dailyrainfall' => 'Daily Rainfall',
|
||||||
|
'importcsv' => 'Import CSV',
|
||||||
|
'exportcsv' => 'Export CSV',
|
||||||
|
|
||||||
//Form
|
//Form
|
||||||
'selectstation' => 'Select Station',
|
'selectstation' => 'Select Station',
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
'passwordupdated' => 'Password updated succesfully',
|
'passwordupdated' => 'Password updated succesfully',
|
||||||
'stationdeleted' => 'Station deleted succesfully',
|
'stationdeleted' => 'Station deleted succesfully',
|
||||||
'userdeleted' => 'User deleted successfully',
|
'userdeleted' => 'User deleted successfully',
|
||||||
|
'linksupdated' => 'CCTV link updated successfully',
|
||||||
|
'stationsimported' => 'Stations imported successfully',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,11 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="button-export d-flex justify-content-around m-3">
|
<div class="button-export d-flex justify-content-around m-3">
|
||||||
<button type="button" class="btn btn-outline-primary btn-sm ms-auto" data-bs-toggle="modal"
|
<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal"
|
||||||
data-bs-target="#addModal">@lang('messages.addstation')</button>
|
data-bs-target="#addModal">@lang('messages.addstation')</button>
|
||||||
|
<button type="button" class="btn btn-outline-success btn-sm" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#importModal">@lang('messages.importcsv')</button>
|
||||||
|
<a href="{{ route('stationmanagement.export.csv') }}" class="btn btn-outline-info btn-sm">@lang('messages.exportcsv')</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@@ -343,10 +346,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Import CSV Modal -->
|
||||||
|
<div class="modal fade" id="importModal" tabindex="-1" aria-labelledby="importModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content border-0 shadow">
|
||||||
|
<form method="POST" action="{{ route('stationmanagement.import.csv') }}" enctype="multipart/form-data">
|
||||||
|
@csrf
|
||||||
|
<div class="modal-header bg-success text-white">
|
||||||
|
<h5 class="modal-title" id="importModalLabel">@lang('messages.importcsv')</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="csv_file">CSV File</label>
|
||||||
|
<input type="file" class="form-control" name="csv_file" id="csv_file" accept=".csv,.txt" required>
|
||||||
|
<div class="form-text">Columns: Station ID, Name, District, Longitude, Latitude, Main River Basin, Sub River Basin, Rainfall (0/1), Water Level (0/1), Siren (0/1), CCTV Link</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">@lang('messages.cancel')</button>
|
||||||
|
<button type="submit" class="btn btn-success">@lang('messages.importcsv')</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -74,6 +74,13 @@ Route::middleware(['admin'])->group(function () {
|
|||||||
Route::post('/stationmanagement/{stationid}/update',[App\Http\Controllers\AdminController::class, 'updateStation'])->name('stationmanagement.update');
|
Route::post('/stationmanagement/{stationid}/update',[App\Http\Controllers\AdminController::class, 'updateStation'])->name('stationmanagement.update');
|
||||||
Route::delete('/stationmanagement/{stationid}/delete',[App\Http\Controllers\AdminController::class, 'deleteStation'])->name('stationmanagement.delete');
|
Route::delete('/stationmanagement/{stationid}/delete',[App\Http\Controllers\AdminController::class, 'deleteStation'])->name('stationmanagement.delete');
|
||||||
|
|
||||||
|
// CCTV Link Management
|
||||||
|
Route::post('/cctv/{stationid}/update',[App\Http\Controllers\cctvController::class, 'updateCctvLink'])->name('cctv.update');
|
||||||
|
|
||||||
|
// Station CSV Import/Export
|
||||||
|
Route::get('/stationmanagement/export-csv',[App\Http\Controllers\AdminController::class, 'exportStations'])->name('stationmanagement.export.csv');
|
||||||
|
Route::post('/stationmanagement/import-csv',[App\Http\Controllers\AdminController::class, 'importStations'])->name('stationmanagement.import.csv');
|
||||||
|
|
||||||
// User Management
|
// User Management
|
||||||
Route::get('/usermgmt',[App\Http\Controllers\AdminController::class, 'userDisplay'])->name('usermgmt');
|
Route::get('/usermgmt',[App\Http\Controllers\AdminController::class, 'userDisplay'])->name('usermgmt');
|
||||||
Route::post('/usermgmt/store',[App\Http\Controllers\AdminController::class, 'storeUser'])->name('usermgmt.store');
|
Route::post('/usermgmt/store',[App\Http\Controllers\AdminController::class, 'storeUser'])->name('usermgmt.store');
|
||||||
|
|||||||
Reference in New Issue
Block a user