QR: add type-to-confirm delete on the QR code page
Deploy Ladill QR Plus / deploy (push) Successful in 59s
Deploy Ladill QR Plus / deploy (push) Successful in 59s
Users asked to delete codes they no longer need. Add a destroy route + controller action (authorized via QrCodePolicy::delete) and a danger-zone delete on the show page behind a confirmation modal that requires typing the code's label — deliberately not a one-click delete since it permanently breaks the printed code and removes its scan history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c4e2c75324
commit
46d0ef8825
@@ -279,6 +279,20 @@ class QrCodeController extends Controller
|
||||
return back()->with('success', 'QR code updated.');
|
||||
}
|
||||
|
||||
public function destroy(Request $request, QrCode $qrCode): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', $qrCode);
|
||||
|
||||
// Remove the code and its scan history. Deletion is gated behind a
|
||||
// type-to-confirm dialog in the UI — this is intentionally permanent.
|
||||
$qrCode->scans()->delete();
|
||||
$qrCode->delete();
|
||||
|
||||
return redirect()
|
||||
->route('user.qr-codes.index')
|
||||
->with('success', 'QR code deleted.');
|
||||
}
|
||||
|
||||
public function stylePreview(Request $request): Response
|
||||
{
|
||||
$validated = $request->validate(array_merge($this->styleRules(), [
|
||||
|
||||
@@ -339,5 +339,52 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Danger zone: delete this QR code (type-to-confirm, not a one-click delete) --}}
|
||||
<div class="rounded-2xl border border-red-200 bg-red-50/40 p-5 shadow-sm"
|
||||
x-data="{ open: false, confirm: '' }">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-red-800">Delete this QR code</h3>
|
||||
<p class="mt-1 text-sm text-red-700/80">The printed code stops working permanently and its scan history is removed. This can't be undone.</p>
|
||||
</div>
|
||||
<button type="button" @click="open = true; confirm = ''"
|
||||
class="inline-flex shrink-0 items-center justify-center rounded-xl border border-red-300 bg-white px-4 py-2 text-sm font-semibold text-red-700 transition hover:bg-red-100">
|
||||
Delete code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div x-show="open" x-cloak
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
@keydown.escape.window="open = false">
|
||||
<div class="absolute inset-0 bg-slate-900/40" @click="open = false"></div>
|
||||
<div class="relative w-full max-w-md rounded-2xl bg-white p-6 shadow-xl">
|
||||
<h3 class="text-base font-bold text-slate-900">Delete “{{ $qrCode->label }}”?</h3>
|
||||
<p class="mt-2 text-sm text-slate-600">
|
||||
This permanently deletes the code and its scan history. Anyone who scans the printed code will get an error.
|
||||
<span class="font-semibold">This cannot be undone.</span>
|
||||
</p>
|
||||
<label class="mt-4 block text-sm font-medium text-slate-700">
|
||||
Type <span class="font-semibold text-slate-900">{{ $qrCode->label }}</span> to confirm
|
||||
</label>
|
||||
<input type="text" x-model="confirm" autocomplete="off" autofocus
|
||||
class="mt-1.5 w-full rounded-xl border border-slate-300 px-3 py-2 text-sm focus:border-red-400 focus:ring-2 focus:ring-red-100 focus:outline-none"
|
||||
placeholder="{{ $qrCode->label }}">
|
||||
<div class="mt-5 flex items-center justify-end gap-2">
|
||||
<button type="button" @click="open = false"
|
||||
class="rounded-xl px-4 py-2 text-sm font-semibold text-slate-600 hover:bg-slate-100">Cancel</button>
|
||||
<form action="{{ route('user.qr-codes.destroy', $qrCode) }}" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit"
|
||||
:disabled="confirm.trim() !== @js($qrCode->label)"
|
||||
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-40">
|
||||
Delete permanently
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</x-user-layout>
|
||||
|
||||
@@ -48,6 +48,7 @@ Route::middleware(['auth'])->group(function () {
|
||||
Route::post('/qr-codes/style-preview', [QrCodeController::class, 'stylePreview'])->name('user.qr-codes.style-preview');
|
||||
Route::get('/qr-codes/{qrCode}', [QrCodeController::class, 'show'])->name('user.qr-codes.show');
|
||||
Route::patch('/qr-codes/{qrCode}', [QrCodeController::class, 'update'])->name('user.qr-codes.update');
|
||||
Route::delete('/qr-codes/{qrCode}', [QrCodeController::class, 'destroy'])->name('user.qr-codes.destroy');
|
||||
Route::post('/qr-codes/{qrCode}/canonical-image', [QrCodeController::class, 'storeCanonicalImage'])->name('user.qr-codes.canonical-image');
|
||||
Route::get('/qr-codes/{qrCode}/preview.png', [QrCodeController::class, 'preview'])->name('user.qr-codes.preview');
|
||||
Route::get('/qr-codes/{qrCode}/download/{format}', [QrCodeController::class, 'download'])->name('user.qr-codes.download')->whereIn('format', ['png', 'svg', 'pdf']);
|
||||
|
||||
Reference in New Issue
Block a user