diff --git a/app/Http/Controllers/Api/GoogleSheetController.php b/app/Http/Controllers/Api/GoogleSheetController.php index 7cad121..baac2ec 100644 --- a/app/Http/Controllers/Api/GoogleSheetController.php +++ b/app/Http/Controllers/Api/GoogleSheetController.php @@ -3,33 +3,15 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; -use App\Services\GoogleSheetService; use Illuminate\Http\Request; class GoogleSheetController extends Controller { - protected $googleSheetService; - public function __construct(GoogleSheetService $googleSheetService){ - $this->googleSheetService = $googleSheetService; - } - /** - * Display a listing of the resource. - */ - public function index() + public function index(Request $request) { - $dataCollection = $this->googleSheetService->getSheetDataCollection(); - $result = [ - "last_row" => $this->googleSheetService->getLastRowByColumn("C"), - "last_column" => $this->googleSheetService->getLastColumn(), - "header" => $this->googleSheetService->getHeader(), - "data_collection" => $dataCollection - ]; - return response()->json($result); + } - /** - * Store a newly created resource in storage. - */ public function store(Request $request) { // diff --git a/app/Http/Controllers/Api/PbgTaskGoogleSheetsController.php b/app/Http/Controllers/Api/PbgTaskGoogleSheetsController.php new file mode 100644 index 0000000..b7a8c49 --- /dev/null +++ b/app/Http/Controllers/Api/PbgTaskGoogleSheetsController.php @@ -0,0 +1,55 @@ +orderBy('id', 'desc'); + if ($request->filled('search')) { + $query->where('no_registrasi', 'like', "%{$request->get('search')}%"); + } + return PbgTaskGoogleSheetResource::collection($query->paginate(config('app.paginate_per_page', 50))); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 8677cd5..f1aab11 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -2,7 +2,54 @@ namespace App\Http\Controllers; +use Illuminate\Support\Facades\Auth; + abstract class Controller { - // + protected array $permissions = []; + + public function __construct() + { + if (!Auth::check()) { + return; + } + $this->setUserPermissions(); + } + + protected function setUserPermissions() + { + $user = Auth::user(); + + if (!$user) { + return; + } + + $menus = $user->roles() + ->with(['menus' => function ($query) { + $query->select('menus.id', 'menus.name') + ->withPivot(['allow_show' ,'allow_create', 'allow_update', 'allow_destroy']); + }]) + ->get() + ->pluck('menus') + ->flatten() + ->unique('id'); + + // Store permissions in an associative array + foreach ($menus as $menu) { + $this->permissions[$menu->id] = [ + 'allow_show' => $menu->pivot->allow_show ?? 0, + 'allow_create' => $menu->pivot->allow_create ?? 0, + 'allow_update' => $menu->pivot->allow_update ?? 0, + 'allow_destroy' => $menu->pivot->allow_destroy ?? 0, + ]; + } + + // Share permissions globally in views + view()->share('permissions', $this->permissions); + } + + public function getPermissions() + { + return $this->permissions; + } } diff --git a/app/Http/Controllers/Data/GoogleSheetsController.php b/app/Http/Controllers/Data/GoogleSheetsController.php new file mode 100644 index 0000000..50eb186 --- /dev/null +++ b/app/Http/Controllers/Data/GoogleSheetsController.php @@ -0,0 +1,34 @@ +query('menu_id'); + $user_menu_permission = $this->permissions[$menu_id]; + return view('data.google-sheet.index', compact('user_menu_permission')); + } + + public function create() + { + return view('data.google-sheet.create'); + } + + public function show(string $id) + { + return view('data.google-sheet.show'); + } + + public function edit(string $id) + { + return view('data.google-sheet.edit'); + } +} diff --git a/app/Http/Resources/PbgTaskGoogleSheetResource.php b/app/Http/Resources/PbgTaskGoogleSheetResource.php new file mode 100644 index 0000000..ce53cc8 --- /dev/null +++ b/app/Http/Resources/PbgTaskGoogleSheetResource.php @@ -0,0 +1,19 @@ + + */ + public function toArray(Request $request): array + { + return parent::toArray($request); + } +} diff --git a/app/Models/RoleMenu.php b/app/Models/RoleMenu.php index 4208269..16e10ea 100644 --- a/app/Models/RoleMenu.php +++ b/app/Models/RoleMenu.php @@ -18,6 +18,13 @@ class RoleMenu extends Model 'allow_destroy', ]; + protected $casts = [ + 'allow_show' => 'boolean', + 'allow_create' => 'boolean', + 'allow_update' => 'boolean', + 'allow_destroy' => 'boolean', + ]; + public $timestamps = true; public function role(){ diff --git a/database/seeders/UsersRoleMenuSeeder.php b/database/seeders/UsersRoleMenuSeeder.php index 22a6be3..97b39cd 100644 --- a/database/seeders/UsersRoleMenuSeeder.php +++ b/database/seeders/UsersRoleMenuSeeder.php @@ -215,6 +215,13 @@ class UsersRoleMenuSeeder extends Seeder "parent_id" => $data->id, "sort_order" => 7, ], + [ + "name" => "Google Sheets", + "url" => "google-sheets", + "icon" => null, + "parent_id" => $data->id, + "sort_order" => 8, + ], [ "name" => "Lap Pariwisata", "url" => "tourisms-report.index", @@ -277,6 +284,7 @@ class UsersRoleMenuSeeder extends Seeder $chatbot = Menu::where('name', 'Chat')->first(); $dalam_sistem = Menu::where('name', 'Dalam Sistem')->first(); $luar_sistem = Menu::where('name', 'Luar Sistem')->first(); + $google_sheets = Menu::where('name', 'Google Sheets')->first(); // Superadmin gets all menus $superadmin->menus()->sync([ @@ -310,6 +318,7 @@ class UsersRoleMenuSeeder extends Seeder $luar_sistem->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $bigdata_resume->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $chatbot->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $google_sheets->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], ]); // Admin gets limited menus diff --git a/resources/js/data/google-sheet/create.js b/resources/js/data/google-sheet/create.js new file mode 100644 index 0000000..e69de29 diff --git a/resources/js/data/google-sheet/edit.js b/resources/js/data/google-sheet/edit.js new file mode 100644 index 0000000..e69de29 diff --git a/resources/js/data/google-sheet/index.js b/resources/js/data/google-sheet/index.js new file mode 100644 index 0000000..3d301b6 --- /dev/null +++ b/resources/js/data/google-sheet/index.js @@ -0,0 +1,211 @@ +import { Grid } from "gridjs/dist/gridjs.umd.js"; +import gridjs from "gridjs/dist/gridjs.umd.js"; +import "gridjs/dist/gridjs.umd.js"; +import GlobalConfig from "../../global-config.js"; + +class GoogleSheets { + constructor() { + this.toastMessage = document.getElementById("toast-message"); + this.toastElement = document.getElementById("toastNotification"); + this.toast = new bootstrap.Toast(this.toastElement); + this.table = null; + + // Initialize functions + this.initTableGoogleSheets(); + this.initEvents(); + } + + initEvents() { + document.body.addEventListener("click", async (event) => { + const deleteButton = event.target.closest( + ".btn-delete-google-sheet" + ); + if (deleteButton) { + event.preventDefault(); + await this.handleDelete(deleteButton); + } + }); + } + + initTableGoogleSheets() { + let tableContainer = document.getElementById( + "table-data-google-sheets" + ); + + if (!tableContainer) { + console.error("Table container not found!"); + return; + } + + // Clear previous table content + tableContainer.innerHTML = ""; + + // Get user permissions from data attributes + let canUpdate = tableContainer.getAttribute("data-updater") === "1"; + let canDelete = tableContainer.getAttribute("data-destroyer") === "1"; + + this.table = new Grid({ + columns: [ + "ID", + "No Registratsi", + "No KRK", + "Format STS", + "Fungsi BG", + "Selesai Terbit", + "Selesai Verifikasi", + "Tanggal Permohonan", + { + name: "Action", + formatter: (cell) => { + let buttons = ""; + + buttons += ` + + + + `; + + if (canUpdate) { + buttons += ` + + + + `; + } + + if (canDelete) { + buttons += ` + + `; + } + + if (!canUpdate && !canDelete) { + buttons = `No Privilege`; + } + + return gridjs.html( + `
${buttons}
` + ); + }, + }, + ], + pagination: { + limit: 50, + server: { + url: (prev, page) => { + let separator = prev.includes("?") ? "&" : "?"; + return `${prev}${separator}page=${page + 1}`; + }, + }, + }, + sort: true, + search: { + server: { + url: (prev, keyword) => { + let separator = prev.includes("?") ? "&" : "?"; + return `${prev}${separator}search=${encodeURIComponent( + keyword + )}`; + }, + }, + debounceTimeout: 1000, + }, + server: { + url: `${GlobalConfig.apiHost}/api/pbg-task-google-sheet`, + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + }, + then: (data) => { + if (!data || !data.data) { + console.warn("⚠️ No data received from API"); + return []; + } + + return data.data.map((item) => { + console.log("🔹 Processing Item:", item); + return [ + item.id, + item.no_registrasi, + item.no_krk, + item.format_sts, + item.fungsi_bg, + item.selesai_terbit, + item.selesai_verifikasi, + item.tgl_permohonan, + item.id, + ]; + }); + }, + total: (data) => { + let totalRecords = data?.meta?.total || 0; + return totalRecords; + }, + catch: (error) => { + console.error("❌ Error fetching data:", error); + }, + }, + }).render(tableContainer); + } + + async handleDelete(deleteButton) { + const id = deleteButton.getAttribute("data-id"); + + const result = await Swal.fire({ + title: "Are you sure?", + text: "You won't be able to revert this!", + icon: "warning", + showCancelButton: true, + confirmButtonColor: "#3085d6", + cancelButtonColor: "#d33", + confirmButtonText: "Yes, delete it!", + }); + + if (result.isConfirmed) { + try { + let response = await fetch( + `${GlobalConfig.apiHost}/api/pbg-task-google-sheet/${id}`, + { + method: "DELETE", + credentials: "include", + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + }, + } + ); + + if (response.ok) { + let result = await response.json(); + this.toastMessage.innerText = + result.message || "Deleted successfully!"; + this.toast.show(); + + // Refresh Grid.js table + if (typeof this.table !== "undefined") { + this.table.updateConfig({}).forceRender(); + } + } else { + let error = await response.json(); + console.error("Delete failed:", error); + this.toastMessage.innerText = + error.message || "Delete failed!"; + this.toast.show(); + } + } catch (error) { + console.error("Error deleting item:", error); + this.toastMessage.innerText = "An error occurred!"; + this.toast.show(); + } + } + } +} +document.addEventListener("DOMContentLoaded", function (e) { + new GoogleSheets(); +}); diff --git a/resources/views/data/google-sheet/create.blade.php b/resources/views/data/google-sheet/create.blade.php new file mode 100644 index 0000000..e831906 --- /dev/null +++ b/resources/views/data/google-sheet/create.blade.php @@ -0,0 +1,162 @@ +@extends('layouts.vertical', ['subtitle' => 'Menu']) + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu']) + + +
+
+
+ Back +
+
+ @csrf +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ +@endsection + +@section('scripts') +@endsection \ No newline at end of file diff --git a/resources/views/data/google-sheet/edit.blade.php b/resources/views/data/google-sheet/edit.blade.php new file mode 100644 index 0000000..509c120 --- /dev/null +++ b/resources/views/data/google-sheet/edit.blade.php @@ -0,0 +1,3 @@ +
+ +
diff --git a/resources/views/data/google-sheet/index.blade.php b/resources/views/data/google-sheet/index.blade.php new file mode 100644 index 0000000..470c3c9 --- /dev/null +++ b/resources/views/data/google-sheet/index.blade.php @@ -0,0 +1,35 @@ +@extends('layouts.vertical', ['subtitle' => 'Google Sheets']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) +@endsection + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'Google Sheets']) + + + +
+
+
+
+
+ @if ($user_menu_permission['allow_create']) + Create + @endif +
+
+
+
+
+
+
+ +@endsection + +@section('scripts') +@vite(['resources/js/data/google-sheet/index.js']) +@endsection \ No newline at end of file diff --git a/resources/views/data/google-sheet/show.blade.php b/resources/views/data/google-sheet/show.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/menus/create.blade.php b/resources/views/menus/create.blade.php index 83bf331..72cd7e4 100644 --- a/resources/views/menus/create.blade.php +++ b/resources/views/menus/create.blade.php @@ -1,9 +1,5 @@ @extends('layouts.vertical', ['subtitle' => 'Menu']) -@section('css') -@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) -@endsection - @section('content') @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu']) diff --git a/routes/api.php b/routes/api.php index f8f72db..2c9d2b2 100644 --- a/routes/api.php +++ b/routes/api.php @@ -11,6 +11,7 @@ use App\Http\Controllers\Api\ImportDatasourceController; use App\Http\Controllers\Api\LackOfPotentialController; use App\Http\Controllers\Api\MenusController; use App\Http\Controllers\Api\PbgTaskController; +use App\Http\Controllers\Api\PbgTaskGoogleSheetsController; use App\Http\Controllers\Api\RequestAssignmentController; use App\Http\Controllers\Api\RolesController; use App\Http\Controllers\Api\ScrapingController; @@ -146,4 +147,7 @@ Route::group(['middleware' => 'auth:sanctum'], function (){ Route::controller(TaskAssignmentsController::class)->group(function (){ Route::get('/task-assignments/{uuid}', 'index')->name('api.task-assignments'); }); + + // pbg-task-google-sheet + Route::apiResource('pbg-task-google-sheet', PbgTaskGoogleSheetsController::class); }); \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 5e57eea..35dd1e3 100755 --- a/routes/web.php +++ b/routes/web.php @@ -19,6 +19,7 @@ use App\Http\Controllers\Data\AdvertisementController; use App\Http\Controllers\Data\UmkmController; use App\Http\Controllers\Data\TourismController; use App\Http\Controllers\Data\SpatialPlanningController; +use App\Http\Controllers\Data\GoogleSheetsController; use App\Http\Controllers\Report\ReportTourismController; use App\Http\Controllers\Chatbot\ChatbotController; use App\Http\Controllers\ChatbotPimpinan\ChatbotPimpinanController; @@ -114,6 +115,13 @@ Route::group(['middleware' => 'auth'], function(){ Route::get('/customers/{customer_id}/edit', 'edit')->name('customers.edit'); Route::get('/customers/upload', 'upload')->name('customers.upload'); }); + + Route::controller(GoogleSheetsController::class)->group(function (){ + Route::get('/google-sheets', 'index')->name('google-sheets'); + Route::get('/google-sheets/create', 'create')->name('google-sheets.create'); + Route::get('/google-sheets/{google_sheet_id}', 'show')->name('google-sheets.show'); + Route::get('/google-sheets/{google_sheet_id}/edit', 'edit')->name('google-sheets.edit'); + }); }); // Report diff --git a/vite.config.js b/vite.config.js index 6c03bc6..8f21633 100755 --- a/vite.config.js +++ b/vite.config.js @@ -106,6 +106,8 @@ export default defineConfig({ //pbg-task "resources/js/pbg-task/index.js", "resources/js/pbg-task/show.js", + // google-sheets + "resources/js/data/google-sheet/index.js", ], refresh: true, }),