feat: add signer name and title fields to invoices with toggleable UI visibility
This commit is contained in:
@@ -46,6 +46,8 @@ class InvoiceController extends Controller
|
||||
'due_date' => 'required|date|after_or_equal:issue_date',
|
||||
'notes' => 'nullable|string',
|
||||
'signature_image' => 'nullable|image|max:2048',
|
||||
'signer_name' => 'nullable|string|max:255',
|
||||
'signer_title' => 'nullable|string|max:255',
|
||||
'bank_name' => 'nullable|string|max:100',
|
||||
'bank_account' => 'nullable|string|max:50',
|
||||
'bank_holder' => 'nullable|string|max:100',
|
||||
@@ -108,6 +110,8 @@ class InvoiceController extends Controller
|
||||
'paid_date' => 'nullable|date',
|
||||
'notes' => 'nullable|string',
|
||||
'signature_image' => 'nullable|image|max:2048',
|
||||
'signer_name' => 'nullable|string|max:255',
|
||||
'signer_title' => 'nullable|string|max:255',
|
||||
'bank_name' => 'nullable|string|max:100',
|
||||
'bank_account' => 'nullable|string|max:50',
|
||||
'bank_holder' => 'nullable|string|max:100',
|
||||
|
||||
@@ -21,7 +21,7 @@ class Invoice extends Model
|
||||
'subtotal', 'tax_rate', 'tax_amount', 'total', 'currency',
|
||||
'status', 'issue_date', 'due_date', 'paid_date', 'notes',
|
||||
'bank_name', 'bank_account', 'bank_holder', 'branch_name',
|
||||
'signature_image',
|
||||
'signature_image', 'signer_name', 'signer_title',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->string('signer_name')->nullable()->after('signature_image');
|
||||
$table->string('signer_title')->nullable()->after('signer_name');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->dropColumn(['signer_name', 'signer_title']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -20,11 +20,27 @@ if [ ! -f .env ]; then
|
||||
fi
|
||||
|
||||
# ── 2. Generate APP_KEY if empty ────────────
|
||||
if grep -qE '^APP_KEY=$' .env 2>/dev/null; then
|
||||
echo "[key] Generating application key…"
|
||||
php artisan key:generate --force --quiet
|
||||
if ! grep -qE '^APP_KEY=base64:' .env 2>/dev/null; then
|
||||
echo "[key] Generating application key directly…"
|
||||
|
||||
# 2a. Clear cached configs that might cause MissingAppKeyException
|
||||
php artisan config:clear --quiet 2>/dev/null || true
|
||||
|
||||
# 2b. Generate native base64 key
|
||||
NEW_KEY="base64:$(php -r 'echo base64_encode(random_bytes(32));')"
|
||||
|
||||
if grep -q '^APP_KEY=' .env 2>/dev/null; then
|
||||
# Replace existing empty/invalid APP_KEY safely
|
||||
sed -i "s|^APP_KEY=.*|APP_KEY=$NEW_KEY|" .env
|
||||
else
|
||||
# Append nicely
|
||||
echo "APP_KEY=$NEW_KEY" >> .env
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2c. Force Laravel to read from .env by ditching Docker's empty OS injection
|
||||
unset APP_KEY
|
||||
|
||||
# ── 3. Ensure SQLite database file exists ───
|
||||
if [ ! -f database/database.sqlite ]; then
|
||||
echo "[db] Creating SQLite database file…"
|
||||
|
||||
@@ -123,6 +123,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 flex items-center gap-2 bg-pink-50/50 p-4 rounded-2xl border border-pink-100">
|
||||
<input type="hidden" name="use_custom_fields" value="0">
|
||||
<input type="checkbox" id="useCustomFields" name="use_custom_fields" value="1" x-model="showCustomFields" class="w-4 h-4 text-pink-600 bg-white border-pink-300 rounded focus:ring-pink-500 cursor-pointer">
|
||||
<label for="useCustomFields" class="text-sm font-bold text-gray-700 cursor-pointer select-none">Show Custom Bank & Signature Settings</label>
|
||||
</div>
|
||||
|
||||
<div x-show="showCustomFields" x-collapse>
|
||||
{{-- Bank Info --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5">
|
||||
<div>
|
||||
@@ -151,10 +158,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes / Payment Terms</label>
|
||||
<textarea name="notes" rows="2" class="input-soft resize-none"
|
||||
placeholder="Payment terms, etc.">{{ old('notes', $invoiceSettings->default_notes) }}</textarea>
|
||||
{{-- Custom Signature Text --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5">
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Signer Name</label>
|
||||
<input type="text" name="signer_name" value="{{ old('signer_name') }}" class="input-soft text-sm" placeholder="e.g. Cathy">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Signer Title</label>
|
||||
<input type="text" name="signer_title" value="{{ old('signer_title') }}" class="input-soft text-sm" placeholder="e.g. Director">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
@@ -167,6 +180,13 @@
|
||||
file:bg-pink-50 file:text-pink-600
|
||||
hover:file:bg-pink-100 cursor-pointer">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes / Payment Terms</label>
|
||||
<textarea name="notes" rows="2" class="input-soft resize-none"
|
||||
placeholder="Payment terms, etc.">{{ old('notes', $invoiceSettings->default_notes) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<button type="submit" class="btn-sakura flex-1 py-3">🧾 Create Invoice</button>
|
||||
@@ -183,6 +203,7 @@
|
||||
<script>
|
||||
function invoiceForm() {
|
||||
return {
|
||||
showCustomFields: {{ old('use_custom_fields', false) ? 'true' : 'false' }},
|
||||
items: [{ description: @json($invoiceSettings->default_service_description ?? ''), quantity: 1, unit_price: 0, is_taxable: true }],
|
||||
taxRate: {{ $invoiceSettings->default_tax_rate ?? 11 }},
|
||||
get subtotal() {
|
||||
|
||||
@@ -121,6 +121,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 flex items-center gap-2 bg-pink-50/50 p-4 rounded-2xl border border-pink-100">
|
||||
<input type="hidden" name="use_custom_fields" value="0">
|
||||
<input type="checkbox" id="useCustomFields" name="use_custom_fields" value="1" x-model="showCustomFields" class="w-4 h-4 text-pink-600 bg-white border-pink-300 rounded focus:ring-pink-500 cursor-pointer">
|
||||
<label for="useCustomFields" class="text-sm font-bold text-gray-700 cursor-pointer select-none">Show Custom Bank & Signature Settings</label>
|
||||
</div>
|
||||
|
||||
<div x-show="showCustomFields" x-collapse>
|
||||
{{-- Bank Info --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5">
|
||||
<div>
|
||||
@@ -145,9 +152,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes / Payment Terms</label>
|
||||
<textarea name="notes" rows="2" class="input-soft resize-none">{{ old('notes', $invoice->notes) }}</textarea>
|
||||
{{-- Custom Signature Text --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5">
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Signer Name</label>
|
||||
<input type="text" name="signer_name" value="{{ old('signer_name', $invoice->signer_name) }}" class="input-soft text-sm" placeholder="e.g. Cathy">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Signer Title</label>
|
||||
<input type="text" name="signer_title" value="{{ old('signer_title', $invoice->signer_title) }}" class="input-soft text-sm" placeholder="e.g. Director">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5" x-data="{
|
||||
@@ -179,6 +193,12 @@
|
||||
file:bg-pink-50 file:text-pink-600
|
||||
hover:file:bg-pink-100 cursor-pointer">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes / Payment Terms</label>
|
||||
<textarea name="notes" rows="2" class="input-soft resize-none">{{ old('notes', $invoice->notes) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<button type="submit" class="btn-sakura flex-1 py-3">💾 Save Changes</button>
|
||||
@@ -200,6 +220,7 @@ function invoiceEditForm() {
|
||||
});
|
||||
@endphp
|
||||
return {
|
||||
showCustomFields: {{ old('use_custom_fields', ($invoice->bank_name || $invoice->signature_image || $invoice->signer_name || $invoice->signer_title) ? true : false) ? 'true' : 'false' }},
|
||||
items: @json($itemsJson),
|
||||
taxRate: {{ $invoice->tax_rate }},
|
||||
get subtotal() {
|
||||
|
||||
@@ -373,11 +373,11 @@
|
||||
</div>
|
||||
<img src="{{ storage_path('app/public/' . $invoice->signature_image) }}" style="max-height: 80px; max-width: 180px; object-fit: contain; margin-bottom: 8px;">
|
||||
<div style="font-size: 12px; font-weight: 800; color: #111827; text-transform: uppercase;">
|
||||
{{ $invoiceSettings->signer_name ?: ($invoiceSettings->header_name ?: 'Signature') }}
|
||||
{{ $invoice->signer_name ?: ($invoiceSettings->signer_name ?: ($invoiceSettings->header_name ?: 'Signature')) }}
|
||||
</div>
|
||||
@if($invoiceSettings->signer_title || $invoiceSettings->header_title)
|
||||
@if($invoice->signer_title || $invoiceSettings->signer_title || $invoiceSettings->header_title)
|
||||
<div style="font-size: 10px; color: #6B7280; text-transform: uppercase; margin-top: 3px;">
|
||||
{{ $invoiceSettings->signer_title ?: $invoiceSettings->header_title }}
|
||||
{{ $invoice->signer_title ?: ($invoiceSettings->signer_title ?: $invoiceSettings->header_title) }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -160,11 +160,11 @@
|
||||
</div>
|
||||
<img src="{{ '/storage/' . $invoice->signature_image }}" alt="Signature" class="h-20 object-contain mb-3 mx-auto">
|
||||
<div class="text-sm font-extrabold text-gray-800 uppercase tracking-wide">
|
||||
{{ $invoiceSettings->signer_name ?: ($invoiceSettings->header_name ?: 'Signature') }}
|
||||
{{ $invoice->signer_name ?: ($invoiceSettings->signer_name ?: ($invoiceSettings->header_name ?: 'Signature')) }}
|
||||
</div>
|
||||
@if($invoiceSettings->signer_title || $invoiceSettings->header_title)
|
||||
@if($invoice->signer_title || $invoiceSettings->signer_title || $invoiceSettings->header_title)
|
||||
<div class="text-xs text-gray-400 uppercase mt-0.5 tracking-wider">
|
||||
{{ $invoiceSettings->signer_title ?: $invoiceSettings->header_title }}
|
||||
{{ $invoice->signer_title ?: ($invoiceSettings->signer_title ?: $invoiceSettings->header_title) }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user