Add chunked uploads and raise storage rate to GHS 0.30/GB/month.
Deploy Ladill Transfer / deploy (push) Successful in 39s

Large files upload in 5 MB chunks (hosting file manager pattern) for the
transfer UI and Ladill Mail S2S API, with no app-level file size cap when
TRANSFER_MAX_FILE_BYTES=0.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-08 12:07:27 +00:00
co-authored by Cursor
parent fb131cd7fa
commit 65b634bb0b
23 changed files with 797 additions and 27 deletions
+1 -1
View File
@@ -58,7 +58,7 @@
<div class="rounded-2xl border border-indigo-100 bg-indigo-50/60 p-6">
<p class="text-xs font-medium uppercase tracking-wide text-indigo-600">Wallet balance</p>
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ $fmt($balanceMinor) }}</p>
<p class="mt-2 text-sm text-slate-600">Storage is billed at GHS {{ number_format((float) config('transfer.price_per_gb_month', 0.15), 2) }} per GB per month from your Ladill wallet.</p>
<p class="mt-2 text-sm text-slate-600">Storage is billed at GHS {{ number_format((float) config('transfer.price_per_gb_month', 0.30), 2) }} per GB per month from your Ladill wallet.</p>
<div class="mt-4 space-y-3">
<a href="{{ route('transfer.transfers.create') }}" class="flex items-center justify-between rounded-xl border border-white bg-white px-4 py-3 text-sm hover:bg-indigo-50/50">
<span class="font-medium text-slate-700">New transfer</span>
@@ -11,7 +11,15 @@
<div class="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{{ session('error') }}</div>
@endif
<form method="post" action="{{ route('transfer.transfers.store') }}" enctype="multipart/form-data" class="space-y-5 rounded-2xl border border-slate-200 bg-white p-6">
<form method="post" action="{{ route('transfer.transfers.store') }}" enctype="multipart/form-data"
x-data="transferCreateForm(@js([
'initUrl' => route('transfer.transfers.upload.init'),
'chunkUrl' => route('transfer.transfers.upload.chunk'),
'finalizeUrl' => route('transfer.transfers.upload.finalize'),
'csrfToken' => csrf_token(),
]))"
@submit.prevent="submit"
class="space-y-5 rounded-2xl border border-slate-200 bg-white p-6">
@csrf
<div>
<label for="title" class="block text-sm font-medium text-slate-700">Title</label>
@@ -28,9 +36,17 @@
<div>
<label for="files" class="block text-sm font-medium text-slate-700">Files</label>
<input type="file" name="files[]" id="files" multiple required
<input type="file" id="files" multiple required @change="onFilesChange"
class="mt-1 block w-full text-sm text-slate-600 file:mr-4 file:rounded-lg file:border-0 file:bg-indigo-50 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-indigo-700 hover:file:bg-indigo-100">
<p class="mt-1 text-xs text-slate-500">Up to {{ $maxFiles }} files. Max 500 MB each.</p>
<p class="mt-1 text-xs text-slate-500">Up to {{ $maxFiles }} files. Large files upload in small chunks no practical size limit.</p>
<template x-if="uploading">
<div class="mt-2">
<div class="h-2 w-full overflow-hidden rounded-full bg-slate-100">
<div class="h-full rounded-full bg-indigo-600 transition-all" :style="`width: ${uploadProgress}%`"></div>
</div>
<p class="mt-1 text-xs text-slate-500" x-text="uploadLabel"></p>
</div>
</template>
@error('files')<p class="mt-1 text-xs text-red-600">{{ $message }}</p>@enderror
@error('files.*')<p class="mt-1 text-xs text-red-600">{{ $message }}</p>@enderror
</div>
@@ -52,7 +68,11 @@
<div class="flex justify-end gap-3 pt-2">
<a href="{{ route('transfer.transfers.index') }}" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a>
<button type="submit" class="rounded-xl bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Create transfer</button>
<button type="submit" class="rounded-xl bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700 disabled:opacity-60"
:disabled="uploading || submitting">
<span x-show="!uploading && !submitting">Create transfer</span>
<span x-show="uploading || submitting" x-cloak>Uploading…</span>
</button>
</div>
</form>
</div>