Fix admin AI nav bug, add rescan feature

- Fix Blade parse error in admin AI settings (Blade was interpreting
  JSON braces in DEFAULT_PROMPT as template tags, breaking the navbar)
  — pass defaultPrompt from controller instead
- Add rescan: POST /nail-polishes/{id}/rescan creates a new import for
  an existing nail polish and updates its fields after AI analysis
- Add KI-Rescan button on collection cards (only if image exists)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Housemann
2026-06-13 18:22:41 +02:00
co-authored by Claude Sonnet 4.6
parent f60221d0ef
commit 9861a74e1e
6 changed files with 57 additions and 19 deletions
@@ -10,8 +10,9 @@ class AiSettingController extends Controller
{ {
public function show() public function show()
{ {
$ai = AiSetting::get(); $ai = AiSetting::get();
return view('admin.ai.show', compact('ai')); $defaultPrompt = AiSetting::DEFAULT_PROMPT;
return view('admin.ai.show', compact('ai', 'defaultPrompt'));
} }
public function update(Request $request) public function update(Request $request)
@@ -3,6 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Jobs\ProcessNailPolishImage; use App\Jobs\ProcessNailPolishImage;
use App\Models\NailPolish;
use App\Models\NailPolishImport; use App\Models\NailPolishImport;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@@ -63,9 +64,25 @@ class NailPolishImportController extends Controller
public function show(NailPolishImport $nailPolishImport) public function show(NailPolishImport $nailPolishImport)
{ {
// Nur eigene Imports ansehen
abort_if($nailPolishImport->user_id !== auth()->id(), 403); abort_if($nailPolishImport->user_id !== auth()->id(), 403);
return view('nail-polish-imports.show', ['import' => $nailPolishImport->load('nailPolish.manufacturer')]); return view('nail-polish-imports.show', ['import' => $nailPolishImport->load('nailPolish.manufacturer')]);
} }
public function rescan(NailPolish $nailPolish)
{
abort_unless($nailPolish->image_path, 404);
$import = NailPolishImport::create([
'user_id' => auth()->id(),
'image_path' => $nailPolish->image_path,
'status' => 'pending',
'nail_polish_id' => $nailPolish->id,
]);
ProcessNailPolishImage::dispatch($import->id);
return redirect()->route('nail-polish-imports.show', $import)
->with('success', 'KI-Analyse gestartet. Die Daten werden aktualisiert.');
}
} }
+24 -14
View File
@@ -61,22 +61,32 @@ class ProcessNailPolishImage implements ShouldQueue
); );
} }
// Nagellack-Nummer eindeutig machen falls nötig // Rescan: bestehenden Lack updaten
$number = $data['number'] ?? ('import-' . $this->importId); if ($import->nail_polish_id) {
if (NailPolish::where('number', $number)->exists()) { $nailPolish = NailPolish::findOrFail($import->nail_polish_id);
$number .= '-' . $this->importId; $nailPolish->update([
'name' => $data['name'] ?? $nailPolish->name,
'number' => $data['number'] ?? $nailPolish->number,
'manufacturer_id' => $manufacturer?->id ?? $nailPolish->manufacturer_id,
]);
} else {
// Neuen Lack anlegen
$number = $data['number'] ?? ('import-' . $this->importId);
if (NailPolish::where('number', $number)->exists()) {
$number .= '-' . $this->importId;
}
$nailPolish = NailPolish::create([
'name' => $data['name'] ?? 'Unbekannt',
'number' => $number,
'manufacturer_id' => $manufacturer?->id,
'image_path' => $import->image_path,
]);
// Zur Sammlung des Users hinzufügen
$import->user->nailPolishes()->syncWithoutDetaching([$nailPolish->id]);
} }
$nailPolish = NailPolish::create([
'name' => $data['name'] ?? 'Unbekannt',
'number' => $number,
'manufacturer_id' => $manufacturer?->id,
'image_path' => $import->image_path,
]);
// Zur Sammlung des Users hinzufügen
$import->user->nailPolishes()->syncWithoutDetaching([$nailPolish->id]);
$import->update([ $import->update([
'status' => 'done', 'status' => 'done',
'result' => $data, 'result' => $data,
+2 -1
View File
@@ -93,8 +93,9 @@
@section('scripts') @section('scripts')
<script> <script>
const defaultPrompt = @json($defaultPrompt);
function resetPrompt() { function resetPrompt() {
document.getElementById('prompt').value = @json(\App\Models\AiSetting::DEFAULT_PROMPT); document.getElementById('prompt').value = defaultPrompt;
} }
</script> </script>
@endsection @endsection
@@ -65,7 +65,15 @@
<span class="np-brand">{{ $nailPolish->manufacturer->name }}</span> <span class="np-brand">{{ $nailPolish->manufacturer->name }}</span>
@endif @endif
</div> </div>
<div class="mt-3"> <div class="mt-3 d-flex flex-column gap-2">
@if($nailPolish->image_path)
<form method="POST" action="{{ route('nail-polishes.rescan', $nailPolish) }}">
@csrf
<button type="submit" class="btn btn-outline-secondary btn-sm w-100">
<i class="fas fa-sync-alt me-1"></i>KI-Rescan
</button>
</form>
@endif
<form method="POST" action="{{ route('user-nail-polishes.remove', $nailPolish) }}" <form method="POST" action="{{ route('user-nail-polishes.remove', $nailPolish) }}"
onsubmit="return confirm('Lack aus der Sammlung entfernen?')"> onsubmit="return confirm('Lack aus der Sammlung entfernen?')">
@csrf @csrf
+1
View File
@@ -69,6 +69,7 @@ Route::middleware(['auth'])->group(function () {
// KI Foto-Import (für alle eingeloggten User) // KI Foto-Import (für alle eingeloggten User)
Route::resource('nail-polish-imports', NailPolishImportController::class) Route::resource('nail-polish-imports', NailPolishImportController::class)
->only(['index', 'create', 'store', 'show']); ->only(['index', 'create', 'store', 'show']);
Route::post('/nail-polishes/{nailPolish}/rescan', [NailPolishImportController::class, 'rescan'])->name('nail-polishes.rescan');
}); });
// Fallback // Fallback