Files
ladill-transfer/resources/views/transfer/files/index.blade.php
T
isaaccladandCursor 453cb9da24
Deploy Ladill Transfer / deploy (push) Successful in 1m5s
Hide redundant My files breadcrumb on the Files root page.
Only show folder breadcrumbs when navigating inside a folder.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 20:55:02 +00:00

407 lines
29 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-user-layout>
<x-slot name="title">Files</x-slot>
@php
$iconBase = asset('images/ladill-icons');
$fmtBytes = function (int $bytes) {
if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2).' GB';
if ($bytes >= 1048576) return number_format($bytes / 1048576, 1).' MB';
return number_format($bytes / 1024, 0).' KB';
};
$fileRows = $files->map(fn ($file) => [
'id' => $file->id,
'name' => $file->original_name,
'size' => $file->humanSize(),
'downloads' => $file->downloads_total,
'modified' => $file->updated_at?->format('M j, Y') ?? '—',
'shareUrl' => $file->transfer->qrCode?->publicUrl() ?? '',
'downloadUrl' => route('transfer.files.download', $file),
'shared' => $file->transfer->recipient_email !== null || $file->downloads_total > 0,
])->values();
$folderRows = $folders->map(fn ($folder) => [
'id' => $folder->id,
'name' => $folder->title,
'itemCount' => (int) $folder->files_count,
'modified' => $folder->updated_at?->format('M j, Y') ?? '—',
'shareUrl' => $folder->qrCode?->publicUrl() ?? '',
'downloadUrl' => route('transfer.files.folders.download', $folder),
'openUrl' => route('transfer.files.index', ['folder' => $folder->id]),
'shared' => $folder->recipient_email !== null || $folder->downloads_total > 0,
])->values();
@endphp
<div class="space-y-6"
x-data="filesManager(@js([
'fileIds' => $fileRows->pluck('id')->all(),
'files' => $fileRows->all(),
'folderIds' => $folderTransfer || $search !== '' ? [] : $folderRows->pluck('id')->all(),
'folders' => $folderTransfer || $search !== '' ? [] : $folderRows->all(),
'csrf' => csrf_token(),
'currentFolderId' => $folderTransfer?->id,
'routes' => [
'bulkDownload' => route('transfer.files.bulk-download'),
'bulkDelete' => route('transfer.files.bulk-delete'),
'move' => route('transfer.files.move'),
'share' => route('transfer.files.share'),
'openInEmail' => route('transfer.files.open-in-email'),
'uploadCheck' => route('transfer.files.upload.check'),
'upload' => route('transfer.files.upload'),
'filesIndex' => route('transfer.files.index'),
'createFolder' => route('transfer.files.folders.store'),
],
'moveTargets' => $moveTargets->map(fn ($t) => [
'id' => $t->id,
'title' => $t->is_root_storage ? 'My files (root)' : $t->title,
])->values()->all(),
]))">
<div class="relative overflow-hidden rounded-2xl border border-slate-200 bg-white">
<div class="absolute inset-0 bg-gradient-to-br from-sky-50/80 via-white to-indigo-50/60"></div>
<div class="relative px-6 py-8 sm:px-10">
<div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
<div class="max-w-xl">
<div class="inline-flex items-center gap-1.5 rounded-full bg-sky-100 px-3 py-1 text-xs font-semibold text-sky-700">
Cloud storage · QR sharing
</div>
<h1 class="mt-3 text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">Your files</h1>
<p class="mt-2 text-sm leading-6 text-slate-600">
Upload, organise, and share files from your Ladill Transfer storage. Billed at GHS {{ number_format($pricePerGb, 2) }}/GB per month.
</p>
<div class="mt-6">
<div class="relative inline-block" @click.outside="createMenuOpen = false">
<button type="button"
@click="createMenuOpen = !createMenuOpen"
class="btn-primary">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
Create or upload
<svg class="h-4 w-4 opacity-80" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/></svg>
</button>
<div x-show="createMenuOpen"
x-cloak
x-transition
class="absolute left-0 z-30 mt-2 w-56 overflow-hidden rounded-xl border border-slate-200 bg-white py-1 shadow-lg sm:left-auto sm:right-0">
<button type="button" @click="openFolderModal()" class="flex w-full items-center gap-3 px-4 py-2.5 text-left text-sm text-slate-700 hover:bg-slate-50">
<img src="{{ $iconBase }}/folder.svg" alt="" class="h-5 w-5">
<span>Folder</span>
</button>
<div class="my-1 border-t border-slate-100"></div>
<button type="button" @click="triggerFileUpload()" class="flex w-full items-center gap-3 px-4 py-2.5 text-left text-sm text-slate-700 hover:bg-slate-50">
<img src="{{ $iconBase }}/file.svg" alt="" class="h-5 w-5">
<span>Files upload</span>
</button>
<button type="button" @click="triggerFolderUpload()" class="flex w-full items-center gap-3 px-4 py-2.5 text-left text-sm text-slate-700 hover:bg-slate-50">
<span class="relative h-5 w-5">
<img src="{{ $iconBase }}/folder.svg" alt="" class="h-5 w-5">
</span>
<span>Folder upload</span>
</button>
</div>
<input type="file" x-ref="fileUploadInput" multiple class="hidden" @change="handleUploadPick($event)">
<input type="file" x-ref="folderUploadInput" multiple webkitdirectory class="hidden" @change="handleUploadPick($event)">
</div>
</div>
</div>
<div class="scrollbar-hide flex snap-x snap-mandatory gap-3 overflow-x-auto pb-2 sm:grid sm:grid-cols-3 sm:overflow-visible sm:pb-0 lg:gap-4" style="-ms-overflow-style:none;scrollbar-width:none;">
<div class="mobile-stats-card shrink-0 snap-start rounded-xl border border-slate-200 bg-white/90 px-4 py-3 text-center backdrop-blur-sm">
<p class="whitespace-nowrap text-xl font-bold text-slate-900 sm:text-2xl">GHS {{ number_format($balanceMinor / 100, 2) }}</p>
<p class="mt-0.5 whitespace-nowrap text-[11px] font-medium text-slate-500">Account balance</p>
</div>
<div class="mobile-stats-card shrink-0 snap-start rounded-xl border border-slate-200 bg-white/90 px-4 py-3 text-center backdrop-blur-sm">
<p class="whitespace-nowrap text-xl font-bold text-slate-900 sm:text-2xl">{{ $fmtBytes($storageBytes) }}</p>
<p class="mt-0.5 whitespace-nowrap text-[11px] font-medium text-slate-500">Storage used</p>
</div>
<div class="mobile-stats-card shrink-0 snap-start rounded-xl border border-slate-200 bg-white/90 px-4 py-3 text-center backdrop-blur-sm">
<p class="whitespace-nowrap text-xl font-bold text-slate-900 sm:text-2xl">{{ number_format($fileCount) }}</p>
<p class="mt-0.5 whitespace-nowrap text-[11px] font-medium text-slate-500">Files</p>
</div>
</div>
</div>
</div>
</div>
@if($folderTransfer)
<nav class="flex flex-wrap items-center gap-1 text-sm text-slate-500">
<a href="{{ route('transfer.files.index') }}" class="font-medium text-indigo-600 hover:text-indigo-800">My files</a>
<span>/</span>
<span class="font-medium text-slate-900">{{ $folderTransfer->title }}</span>
</nav>
@endif
{{-- Action toolbar + file list --}}
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
<div class="flex flex-wrap items-center gap-2 border-b border-slate-100 px-4 py-2.5">
<template x-if="selectedCount === 0">
<div class="flex flex-wrap items-center gap-2 text-sm">
<button type="button" disabled class="rounded-lg px-3 py-1.5 text-slate-400">Download</button>
<button type="button" disabled class="rounded-lg px-3 py-1.5 text-slate-400">Share</button>
<button type="button" disabled class="rounded-lg px-3 py-1.5 text-slate-400">Copy link</button>
<button type="button" disabled class="rounded-lg px-3 py-1.5 text-slate-400">Delete</button>
<button type="button" disabled class="rounded-lg px-3 py-1.5 text-slate-400">Move to</button>
<button type="button" disabled class="rounded-lg px-3 py-1.5 text-slate-400">Open in email</button>
</div>
</template>
<template x-if="selectedCount > 0">
<div class="flex flex-wrap items-center gap-2 text-sm">
<button type="button" @click="downloadSelected()" class="rounded-lg px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-100">Download</button>
<button type="button" @click="shareSelected()" class="rounded-lg px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-100">Share</button>
<button type="button" @click="copyLinks()" class="rounded-lg px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-100">Copy link</button>
<button type="button" @click="deleteSelected()" class="rounded-lg px-3 py-1.5 font-medium text-red-700 hover:bg-red-50">Delete</button>
<button type="button" @click="moveModalOpen = true" class="rounded-lg px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-100">Move to</button>
<button type="button" @click="openInEmail()" class="btn-primary btn-primary-sm">Open in email</button>
<span class="ml-2 text-xs text-slate-500" x-text="`${selectedCount} selected`"></span>
<button type="button" @click="clearSelection()" class="text-xs text-slate-400 hover:text-slate-600">Clear</button>
</div>
</template>
<div class="ml-auto flex flex-wrap items-center gap-2">
<form method="get" action="{{ route('transfer.files.index') }}" class="flex items-center gap-2">
@if($folderTransfer)
<input type="hidden" name="folder" value="{{ $folderTransfer->id }}">
@endif
<input type="search" name="q" value="{{ $search }}" placeholder="Search files…"
class="w-40 rounded-lg border border-slate-200 px-2.5 py-1.5 text-xs focus:border-indigo-300 focus:outline-none focus:ring-2 focus:ring-indigo-100 sm:w-52">
<select name="sort" onchange="this.form.submit()" class="min-w-[7.5rem] rounded-lg border border-slate-200 py-1.5 pl-3 pr-8 text-xs text-slate-700">
<option value="newest" @selected($sort === 'newest')>Newest</option>
<option value="oldest" @selected($sort === 'oldest')>Oldest</option>
<option value="name" @selected($sort === 'name')>Name</option>
<option value="size" @selected($sort === 'size')>Size</option>
<option value="downloads" @selected($sort === 'downloads')>Downloads</option>
</select>
</form>
</div>
</div>
@if($files->isEmpty() && ($folderTransfer || $folders->isEmpty() || $search !== ''))
<div class="px-6 py-12 text-center text-sm text-slate-500">
@if($search !== '')
No files match your search.
@elseif($folderTransfer)
This folder is empty. Upload files or move items here.
@else
No files yet. Use Create or upload to add files.
@endif
</div>
@else
<table class="min-w-full divide-y divide-slate-100 text-sm">
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
<tr>
<th class="w-10 px-4 py-3">
<input type="checkbox" @change="toggleAll($event)" :checked="allSelected" class="rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
</th>
<th class="px-4 py-3">Name</th>
<th class="hidden px-4 py-3 sm:table-cell">Modified</th>
<th class="px-4 py-3">Size</th>
<th class="hidden px-4 py-3 md:table-cell">Sharing</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
{{-- User-created folders (root only) --}}
@if(! $folderTransfer && $search === '')
@foreach($folders as $folder)
<tr class="transition"
:class="selectedFolderIds.includes({{ $folder->id }}) ? 'bg-indigo-50/80' : 'hover:bg-slate-50/80'">
<td class="px-4 py-3">
<input type="checkbox" value="{{ $folder->id }}" x-model.number="selectedFolderIds" class="rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
</td>
<td class="px-4 py-3">
<div class="flex items-center gap-3">
<a href="{{ route('transfer.files.index', ['folder' => $folder->id]) }}" class="flex min-w-0 flex-1 items-center gap-3">
@include('transfer.files.partials.folder-list-icon', ['size' => 'xs'])
<p class="truncate font-medium text-slate-900">{{ $folder->title }}</p>
</a>
<a href="{{ route('transfer.files.folders.download', $folder) }}" class="ml-auto rounded p-1 text-slate-400 hover:text-indigo-600" title="Download folder">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 7.5 12 3m0 0L7.5 7.5M12 3v13.5"/></svg>
</a>
</div>
</td>
<td class="hidden px-4 py-3 text-slate-600 sm:table-cell">{{ $folder->updated_at?->format('M j, Y') ?? '—' }}</td>
<td class="px-4 py-3 text-slate-600">{{ $folder->files_count }} item{{ $folder->files_count === 1 ? '' : 's' }}</td>
<td class="hidden px-4 py-3 md:table-cell">
@if($folder->recipient_email || $folder->downloads_total > 0)
<span class="inline-flex items-center gap-1 text-xs text-slate-600">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z"/></svg>
Shared
</span>
@else
<span class="text-xs text-slate-400">Private</span>
@endif
</td>
</tr>
@endforeach
@endif
@foreach($files as $file)
<tr class="transition"
:class="selectedFileIds.includes({{ $file->id }}) ? 'bg-indigo-50/80' : 'hover:bg-slate-50/80'">
<td class="px-4 py-3">
<input type="checkbox" value="{{ $file->id }}" x-model.number="selectedFileIds" class="rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
</td>
<td class="px-4 py-3">
<div class="flex items-center gap-3">
@include('transfer.files.partials.file-list-icon', ['size' => 'xs'])
<div class="min-w-0">
<p class="truncate font-medium text-slate-900">{{ $file->original_name }}</p>
</div>
<a href="{{ route('transfer.files.download', $file) }}" class="ml-auto rounded p-1 text-slate-400 hover:text-indigo-600" title="Download">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 7.5 12 3m0 0L7.5 7.5M12 3v13.5"/></svg>
</a>
</div>
</td>
<td class="hidden px-4 py-3 text-slate-600 sm:table-cell">{{ $file->updated_at?->format('M j, Y') ?? '—' }}</td>
<td class="px-4 py-3 text-slate-600">{{ $file->humanSize() }}</td>
<td class="hidden px-4 py-3 md:table-cell">
@if($file->transfer->recipient_email || $file->downloads_total > 0)
<span class="inline-flex items-center gap-1 text-xs text-slate-600">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z"/></svg>
Shared
</span>
@else
<span class="text-xs text-slate-400">Private</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@if($files->isNotEmpty())
<div class="flex items-center justify-between border-t border-slate-100 px-4 py-3 text-xs text-slate-500">
<span>{{ $files->total() }} file{{ $files->total() === 1 ? '' : 's' }}</span>
<span>Showing {{ $files->firstItem() }}{{ $files->lastItem() }}</span>
</div>
@endif
@endif
</div>
@if($files->hasPages())
<div>{{ $files->links() }}</div>
@endif
{{-- Create folder modal --}}
<div x-show="folderModalOpen" x-cloak class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4" @keydown.escape.window="folderModalOpen = false">
<div @click.outside="folderModalOpen = false" class="w-full max-w-md rounded-2xl bg-white p-6 shadow-xl">
<h3 class="text-lg font-semibold text-slate-900">New folder</h3>
<form method="post" action="{{ route('transfer.files.folders.store') }}" class="mt-4 space-y-4">
@csrf
@if($folderTransfer)
<input type="hidden" name="folder" value="{{ $folderTransfer->id }}">
@endif
<input x-ref="folderNameInput" type="text" name="name" x-model="newFolderName" required maxlength="120" placeholder="Folder name"
class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm focus:border-indigo-300 focus:outline-none focus:ring-2 focus:ring-indigo-100">
<div class="flex justify-end gap-2">
<button type="button" @click="folderModalOpen = false" class="rounded-xl px-4 py-2 text-sm font-medium text-slate-600 hover:bg-slate-100">Cancel</button>
<x-btn.create type="submit">Create</x-btn.create>
</div>
</form>
</div>
</div>
{{-- Upload confirmation --}}
<div
x-show="uploadConfirmOpen"
x-cloak
class="fixed inset-0 z-[60] flex items-end justify-center bg-black/40 p-4 sm:items-center"
role="dialog"
aria-modal="true"
aria-labelledby="upload-confirm-title"
@keydown.escape.window="uploadConfirmOpen && cancelUploadConfirm()"
>
<div @click.outside="cancelUploadConfirm()" class="w-full max-w-lg rounded-2xl bg-white p-6 shadow-xl">
<h3 id="upload-confirm-title" class="text-lg font-semibold text-slate-900">
Upload <span x-text="pendingConfirmFiles?.length || 0"></span>
<span x-text="(pendingConfirmFiles?.length || 0) === 1 ? 'file' : 'files'"></span>?
</h3>
<p class="mt-2 text-sm text-slate-600">
<span x-show="uploadConfirmIsFolder">
A folder named
<span class="font-medium text-slate-800">'<span x-text="uploadConfirmFolderName"></span>'</span>
will be created with
<span x-text="pendingConfirmFiles?.length || 0"></span>
<span x-text="(pendingConfirmFiles?.length || 0) === 1 ? 'file' : 'files'"></span>
inside it.
</span>
<span x-show="!uploadConfirmIsFolder">
Selected files will be added to
<span class="font-medium text-slate-800" x-text="currentFolderId ? 'this folder' : 'My files'"></span>.
</span>
</p>
<p class="mt-1 text-xs text-slate-500">
Total size: <span x-text="formatBytes(uploadConfirmTotalBytes())"></span>
</p>
<ul class="mt-4 max-h-36 space-y-1 overflow-y-auto rounded-xl bg-slate-50 px-3 py-2 text-sm">
<template x-for="name in uploadConfirmPreviewNames()" :key="name">
<li class="truncate text-slate-700" x-text="name"></li>
</template>
<li
x-show="uploadConfirmRemainingCount() > 0"
class="text-slate-500"
x-text="'and ' + uploadConfirmRemainingCount() + ' more…'"
></li>
</ul>
<div class="mt-6 flex justify-end gap-2">
<button
type="button"
@click="cancelUploadConfirm()"
class="rounded-xl px-4 py-2 text-sm font-medium text-slate-600 hover:bg-slate-100"
>
Cancel
</button>
<button
type="button"
@click="confirmUpload()"
class="btn-primary"
>
Upload
</button>
</div>
</div>
</div>
{{-- Duplicate file modal --}}
<div x-show="duplicateModalOpen" x-cloak class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
<div class="w-full max-w-lg rounded-2xl bg-white p-6 shadow-xl">
<p class="text-sm text-slate-700">
A file with this name already exists so we couldn't upload
<strong x-text="duplicateFile?.filename"></strong>.
</p>
<p class="mt-2 text-sm text-slate-500">Add it as a new version of the existing file, or keep them both.</p>
<div class="mt-5 flex gap-4">
<button type="button" @click="resolveDuplicate('replace')" class="text-sm font-semibold text-indigo-600 hover:text-indigo-800">Replace</button>
<button type="button" @click="resolveDuplicate('keep_both')" class="text-sm font-semibold text-indigo-600 hover:text-indigo-800">Keep both</button>
</div>
</div>
</div>
{{-- Move modal --}}
<div x-show="moveModalOpen" x-cloak class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4" @keydown.escape.window="moveModalOpen = false">
<div @click.outside="moveModalOpen = false" class="w-full max-w-md rounded-2xl bg-white p-6 shadow-xl">
<h3 class="text-lg font-semibold text-slate-900">Move to folder</h3>
<p class="mt-1 text-sm text-slate-500">Group selected files into a folder you created.</p>
<form method="post" action="{{ route('transfer.files.move') }}" class="mt-4 space-y-4">
@csrf
<template x-for="id in selectedFileIds" :key="id">
<input type="hidden" name="files[]" :value="id">
</template>
<select name="destination" required class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm focus:border-indigo-300 focus:outline-none focus:ring-2 focus:ring-indigo-100">
<option value="">Choose a folder…</option>
@foreach($moveTargets as $target)
<option value="{{ $target->id }}">{{ $target->is_root_storage ? 'My files (root)' : $target->title }}</option>
@endforeach
</select>
<div class="flex justify-end gap-2">
<button type="button" @click="moveModalOpen = false" class="rounded-xl px-4 py-2 text-sm font-medium text-slate-600 hover:bg-slate-100">Cancel</button>
<button type="submit" class="btn-primary">Move</button>
</div>
</form>
</div>
</div>
{{-- Upload overlay --}}
<div x-show="uploading" x-cloak class="fixed inset-0 z-40 flex items-center justify-center bg-black/20">
<div class="rounded-xl bg-white px-5 py-3 text-sm font-medium text-slate-700 shadow-lg">Uploading…</div>
</div>
{{-- Toast --}}
<div x-show="toast" x-cloak x-transition class="fixed bottom-24 left-1/2 z-50 -translate-x-1/2 rounded-xl bg-slate-900 px-4 py-2 text-sm text-white shadow-lg lg:bottom-8" x-text="toast"></div>
</div>
</x-user-layout>