From afc047b0bbe1d34bce78060d064a3eb5ca70c538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Tue, 28 Apr 2026 23:38:35 +0300 Subject: [PATCH] feat: add module generator feature with controller, routing, and UI integration --- .../Controllers/ModuleGeneratorController.php | 138 ++++++++++++++++++ resources/views/admin/inc/menu.blade.php | 4 + .../views/admin/module-generator.blade.php | 134 +++++++++++++++++ routes/web.php | 3 + service-worker.js | 16 +- 5 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/ModuleGeneratorController.php create mode 100644 resources/views/admin/module-generator.blade.php diff --git a/app/Http/Controllers/ModuleGeneratorController.php b/app/Http/Controllers/ModuleGeneratorController.php new file mode 100644 index 0000000..d545bdd --- /dev/null +++ b/app/Http/Controllers/ModuleGeneratorController.php @@ -0,0 +1,138 @@ +middleware('auth'); + } + + public function index() + { + return view('admin.module-generator'); + } + + public function generate(Request $request) + { + $request->validate([ + 'title' => 'required|string|max:255', + 'columns' => 'required|array', + 'columns.*.name' => 'required|string', + 'columns.*.type' => 'required|string', + ]); + + $title = $request->title; + $slug = Str::slug($title); + $tableName = str_replace('-', '_', $slug); + $modelName = Str::studly($slug); + + // 1. Create Migration File + $timestamp = date('Y_m_d_His'); + $migrationFilename = "{$timestamp}_create_{$tableName}_table.php"; + $migrationPath = database_path("migrations/{$migrationFilename}"); + + $migrationContent = "id();\n"; + + foreach ($request->columns as $column) { + $colName = Str::snake($column['name']); + $uiType = $column['type']; + + $dbType = 'string'; + switch ($uiType) { + case 'integer': $dbType = 'integer'; break; + case 'decimal': $dbType = 'decimal'; break; + case 'date': $dbType = 'date'; break; + case 'datetime': $dbType = 'dateTime'; break; + case 'long-text': $dbType = 'longText'; break; + case 'boolean': $dbType = 'boolean'; break; + default: $dbType = 'string'; break; + } + + if ($dbType == 'decimal') { + $migrationContent .= " \$table->decimal('{$colName}', 15, 2)->nullable();\n"; + } else { + $migrationContent .= " \$table->{$dbType}('{$colName}')->nullable();\n"; + } + } + + $migrationContent .= " \$table->timestamps();\n \$table->softDeletes();\n });\n }\n\n public function down()\n {\n Schema::dropIfExists('{$tableName}');\n }\n}\n"; + + File::put($migrationPath, $migrationContent); + + // Run the specific migration + Artisan::call('migrate', [ + '--path' => "database/migrations/{$migrationFilename}", + '--force' => true + ]); + + // 2. Create Model + $modelPath = app_path("Models/{$modelName}.php"); + if (!File::exists($modelPath)) { + $modelContent = "columns as $column) { + $colName = Str::snake($column['name']); + $colType = $column['type']; + $relationDatasCode .= " '{$colName}' => [\n 'type' => '{$colType}',\n ],\n"; + } + $relationDatasCode .= "];"; + + // Replace the hardcoded relationDatas in template + $pattern = '/\$relationDatas = \[.*?\];/s'; + $templateContent = preg_replace($pattern, $relationDatasCode, $templateContent); + + File::put($viewPath, $templateContent); + } + } + + // 4. Handle Icon + $iconName = $slug; + if ($request->hasFile('icon')) { + $request->file('icon')->move(base_path('assets/icons'), $iconName . '.png'); + } + + // 5. Insert into types table + $type = Types::where('slug', $slug)->first(); + if (!$type) { + $type = new Types(); + $type->title = $title; + $type->slug = $slug; + $type->icon = $iconName; + $type->enabled = 1; + $type->full_control = "1"; // Default admin level + $type->write = "1"; + $type->read = "1"; + $type->modify = "1"; + $type->save(); + } + + return redirect("admin/types/{$slug}")->with('success', 'Module generated successfully!'); + } +} diff --git a/resources/views/admin/inc/menu.blade.php b/resources/views/admin/inc/menu.blade.php index b9794d2..1662fc6 100644 --- a/resources/views/admin/inc/menu.blade.php +++ b/resources/views/admin/inc/menu.blade.php @@ -195,6 +195,10 @@ {{e2('Modules')}} + +
  • + {{e2('Module Generator')}}
  • +
    + @csrf +
    +
    +

    {{e2("Module Basic Information")}}

    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + + Recommended size: 64x64px. Saved to assets/icons/ +
    +
    +
    +
    +
    + +
    +
    +

    {{e2("Database Columns")}}

    +
    + +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    {{e2("Column Name")}}{{e2("Type")}}{{e2("Action")}}
    + + + + + +
    +
    +
    + +
    +
    +
    +
    + + +@section('script') + +@endsection + +@endsection diff --git a/routes/web.php b/routes/web.php index a8198f5..5dd47a3 100644 --- a/routes/web.php +++ b/routes/web.php @@ -234,6 +234,9 @@ Route::group(['prefix' => $hash, 'middleware' => 'auth'], function () { return back()->with('success', 'Statuses updated'); }); + Route::get('/module-generator', [\App\Http\Controllers\ModuleGeneratorController::class, 'index']); + Route::post('/module-generator/generate', [\App\Http\Controllers\ModuleGeneratorController::class, 'generate']); + }); // Önbelleğe alınmış dashboard sayfası route'u diff --git a/service-worker.js b/service-worker.js index 2be42ea..4a517a9 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,11 +1,21 @@ self.addEventListener('install', (event) => { event.waitUntil( caches.open('app-cache').then((cache) => { - return cache.addAll([ - '/', + // Use a more robust approach: attempt to cache but don't fail the whole installation if one fails + const urlsToCache = [ '/assets/manifest.json', '/assets/favicon.png' - ]); + ]; + + return Promise.all( + urlsToCache.map(url => { + return fetch(url).then(response => { + if (response.ok) { + return cache.put(url, response); + } + }).catch(err => console.warn('SW: Failed to cache ' + url, err)); + }) + ); }) ); });