change style of button

This commit is contained in:
I-Fun
2026-04-05 09:56:29 +07:00
parent 5656dc1f3a
commit 8b5992b7ce
14 changed files with 1466 additions and 1394 deletions

View File

@@ -5,97 +5,109 @@
@section('page-subtitle', 'Manage your brand deal payments')
@section('content')
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5">
<div class="flex gap-2 shrink-0">
<a href="{{ route('invoice-settings.edit') }}"
class="text-xs px-3 py-2 rounded-full border border-gray-200 text-gray-500 hover:bg-gray-50 flex items-center gap-1">
⚙️ Settings
</a>
<a href="{{ route('invoices.create') }}" class="btn-sakura text-sm">+ New Invoice</a>
<div class="flex gap-2 shrink-0">
<a href="{{ route('invoices.create') }}" class="btn-sakura text-sm">+ New Invoice</a>
<a href="{{ route('invoice-settings.edit') }}"
class="btn-sakura text-sm px-3 py-2 rounded-full border border-gray-200 text-gray-500 hover:bg-gray-50 flex items-center gap-1">
⚙️ Settings
</a>
</div>
</div>
</div>
{{-- Mobile card list --}}
<div class="flex flex-col gap-3 sm:hidden">
@forelse($invoices as $invoice)
<div class="cat-card p-4">
<div class="flex items-start justify-between gap-2 mb-2">
<div>
<div class="font-mono text-sm font-bold text-gray-700">{{ $invoice->invoice_number }}</div>
<div class="text-xs text-gray-400">{{ $invoice->client?->name ?? '—' }}</div>
{{-- Mobile card list --}}
<div class="flex flex-col gap-3 sm:hidden">
@forelse($invoices as $invoice)
<div class="cat-card p-4">
<div class="flex items-start justify-between gap-2 mb-2">
<div>
<div class="font-mono text-sm font-bold text-gray-700">{{ $invoice->invoice_number }}</div>
<div class="text-xs text-gray-400">{{ $invoice->client?->name ?? '—' }}</div>
</div>
<span class="badge {{ $invoice->status->badgeClass() }} shrink-0">{{ $invoice->status->label() }}</span>
</div>
<div class="text-sm text-gray-700 font-semibold mb-1">{{ $invoice->title }}</div>
<div class="flex items-center justify-between text-xs text-gray-500">
<span>Due {{ $invoice->due_date->format('d M Y') }}</span>
<span class="font-bold text-gray-800">{{ $invoice->formatted_total }}</span>
</div>
<div class="flex gap-2 mt-3 pt-3 border-t border-pink-50">
<a href="{{ route('invoices.show', $invoice) }}"
class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-pink-600 hover:bg-pink-50">View</a>
<a href="{{ route('invoices.pdf', $invoice) }}" target="_blank"
class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-pink-600 hover:bg-pink-50">PDF</a>
<a href="{{ route('invoices.edit', $invoice) }}"
class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50">Edit</a>
{{-- Share button disabled --}}
</div>
<span class="badge {{ $invoice->status->badgeClass() }} shrink-0">{{ $invoice->status->label() }}</span>
</div>
<div class="text-sm text-gray-700 font-semibold mb-1">{{ $invoice->title }}</div>
<div class="flex items-center justify-between text-xs text-gray-500">
<span>Due {{ $invoice->due_date->format('d M Y') }}</span>
<span class="font-bold text-gray-800">{{ $invoice->formatted_total }}</span>
@empty
<div class="text-center py-16 text-gray-400">
<span class="text-5xl block mb-2">🧾</span>
<p>No invoices yet. <a href="{{ route('invoices.create') }}" class="text-pink-500 hover:underline">Create
one!</a></p>
</div>
<div class="flex gap-2 mt-3 pt-3 border-t border-pink-50">
<a href="{{ route('invoices.show', $invoice) }}" class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-pink-600 hover:bg-pink-50">View</a>
<a href="{{ route('invoices.pdf', $invoice) }}" target="_blank" class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-pink-600 hover:bg-pink-50">PDF</a>
<a href="{{ route('invoices.edit', $invoice) }}" class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50">Edit</a>
{{-- Share button disabled --}}
</div>
</div>
@empty
<div class="text-center py-16 text-gray-400">
<span class="text-5xl block mb-2">🧾</span>
<p>No invoices yet. <a href="{{ route('invoices.create') }}" class="text-pink-500 hover:underline">Create one!</a></p>
</div>
@endforelse
</div>
{{-- Desktop table --}}
<div class="cat-card overflow-hidden hidden sm:block">
<div class="overflow-x-auto">
<table class="table w-full">
<thead class="bg-pink-50">
<tr>
<th class="px-5 py-4">Invoice #</th>
<th class="px-5 py-4">Client</th>
<th class="px-5 py-4">Title</th>
<th class="px-5 py-4">Total</th>
<th class="px-5 py-4">Due Date</th>
<th class="px-5 py-4">Status</th>
<th class="px-5 py-4">Actions</th>
</tr>
</thead>
<tbody>
@forelse($invoices as $invoice)
<tr class="border-b border-pink-50 hover:bg-pink-50/50 transition-colors">
<td class="px-5 py-4 font-mono text-sm font-semibold text-gray-700">{{ $invoice->invoice_number }}</td>
<td class="px-5 py-4 text-sm text-gray-600">{{ $invoice->client?->name ?? '—' }}</td>
<td class="px-5 py-4 text-sm text-gray-700 max-w-xs truncate">{{ $invoice->title }}</td>
<td class="px-5 py-4 font-bold text-gray-800 text-sm">{{ $invoice->formatted_total }}</td>
<td class="px-5 py-4 text-sm text-gray-500">
{{ $invoice->due_date->format('d M Y') }}
@if($invoice->due_date->isPast() && $invoice->status->value !== 'paid')
<span class="text-red-400 text-xs block">Overdue!</span>
@endif
</td>
<td class="px-5 py-4"><span class="badge {{ $invoice->status->badgeClass() }}">{{ $invoice->status->label() }}</span></td>
<td class="px-5 py-4">
<div class="flex items-center gap-2">
<a href="{{ route('invoices.show', $invoice) }}" class="text-xs text-pink-500 hover:underline">View</a>
<a href="{{ route('invoices.pdf', $invoice) }}" class="text-xs text-pink-500 hover:underline" target="_blank">PDF</a>
<a href="{{ route('invoices.edit', $invoice) }}" class="text-xs text-gray-500 hover:underline">Edit</a>
{{-- Share button disabled --}}
</div>
</td>
</tr>
@empty
<tr><td colspan="7" class="text-center py-16 text-gray-400">
<span class="text-4xl block mb-2">🧾</span>
No invoices yet.
</td></tr>
@endforelse
</tbody>
</table>
@endforelse
</div>
</div>
<div class="mt-4">{{ $invoices->links() }}</div>
{{-- Share scripts disabled --}}
@endsection
{{-- Desktop table --}}
<div class="cat-card overflow-hidden hidden sm:block">
<div class="overflow-x-auto">
<table class="table w-full">
<thead class="bg-pink-50">
<tr>
<th class="px-5 py-4">Invoice #</th>
<th class="px-5 py-4">Client</th>
<th class="px-5 py-4">Title</th>
<th class="px-5 py-4">Total</th>
<th class="px-5 py-4">Due Date</th>
<th class="px-5 py-4">Status</th>
<th class="px-5 py-4">Actions</th>
</tr>
</thead>
<tbody>
@forelse($invoices as $invoice)
<tr class="border-b border-pink-50 hover:bg-pink-50/50 transition-colors">
<td class="px-5 py-4 font-mono text-sm font-semibold text-gray-700">{{ $invoice->invoice_number }}
</td>
<td class="px-5 py-4 text-sm text-gray-600">{{ $invoice->client?->name ?? '—' }}</td>
<td class="px-5 py-4 text-sm text-gray-700 max-w-xs truncate">{{ $invoice->title }}</td>
<td class="px-5 py-4 font-bold text-gray-800 text-sm">{{ $invoice->formatted_total }}</td>
<td class="px-5 py-4 text-sm text-gray-500">
{{ $invoice->due_date->format('d M Y') }}
@if($invoice->due_date->isPast() && $invoice->status->value !== 'paid')
<span class="text-red-400 text-xs block">Overdue!</span>
@endif
</td>
<td class="px-5 py-4"><span
class="badge {{ $invoice->status->badgeClass() }}">{{ $invoice->status->label() }}</span>
</td>
<td class="px-5 py-4">
<div class="flex items-center gap-2">
<a href="{{ route('invoices.show', $invoice) }}"
class="text-xs text-pink-500 hover:underline">View</a>
<a href="{{ route('invoices.pdf', $invoice) }}"
class="text-xs text-pink-500 hover:underline" target="_blank">PDF</a>
<a href="{{ route('invoices.edit', $invoice) }}"
class="text-xs text-gray-500 hover:underline">Edit</a>
{{-- Share button disabled --}}
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-16 text-gray-400">
<span class="text-4xl block mb-2">🧾</span>
No invoices yet.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-4">{{ $invoices->links() }}</div>
{{-- Share scripts disabled --}}
@endsection

View File

@@ -5,229 +5,220 @@
@section('page-subtitle', 'Customize number format, header, and invoice defaults')
@section('content')
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-3 mb-5">
<a href="{{ route('invoices.index') }}"
class="text-xs px-3 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
← Back to Invoices
</a>
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-3 mb-5">
<a href="{{ route('invoices.index') }}"
class="btn-sakura text-sm px-3 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
← Back to Invoices
</a>
</div>
@if(session('success'))
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)"
class="flex items-center justify-between gap-3 bg-green-50 border border-green-200 rounded-2xl px-4 py-3 text-sm text-green-700 mb-4">
<span>{{ session('success') }}</span>
<button @click="show = false" class="text-green-500 hover:text-green-700 text-lg leading-none">✕</button>
</div>
@endif
<form action="{{ route('invoice-settings.update') }}" method="POST" enctype="multipart/form-data" class="space-y-5">
@csrf
{{-- Number Format --}}
<div class="cat-card p-5">
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">🔢 Invoice Number Format</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">
Prefix <span class="text-pink-400">*</span>
</label>
<input type="text" name="invoice_prefix"
value="{{ old('invoice_prefix', $settings->invoice_prefix) }}" class="input-soft text-sm"
placeholder="INV-EA" required id="prefix-input" oninput="updatePreview()">
<p class="text-xs text-gray-400 mt-1">e.g. <code>INV-EA</code> → <strong>INV-EA-001</strong></p>
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">
Next Number <span class="text-pink-400">*</span>
</label>
<input type="number" name="invoice_next_number"
value="{{ old('invoice_next_number', $settings->invoice_next_number) }}"
class="input-soft text-sm" min="1" required id="number-input" oninput="updatePreview()">
<p class="text-xs text-gray-400 mt-1">Used for the next new invoice</p>
</div>
</div>
<div class="mt-3 bg-pink-50 rounded-xl px-4 py-2.5 text-sm text-gray-600">
Preview: <strong class="text-pink-600" id="number-preview">
{{ rtrim(old('invoice_prefix', $settings->invoice_prefix), '-') }}-{{ str_pad((string) (old('invoice_next_number', $settings->invoice_next_number) ?? 1), 3, '0', STR_PAD_LEFT) }}
</strong>
</div>
</div>
{{-- Header / Branding --}}
<div class="cat-card p-5">
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">🎨 Invoice Header</h2>
{{-- Logo auto-upload (independent of main Save form) --}}
<div class="mb-4" x-data="{
uploading: false,
errorMsg: '',
logoUrl: '{{ $settings->header_logo ? asset('storage/' . $settings->header_logo) : '' }}',
async pickFile(input) {
if (!input.files.length) return;
this.uploading = true;
this.errorMsg = '';
const fd = new FormData();
fd.append('header_logo', input.files[0]);
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
try {
const res = await fetch('{{ route('invoice-settings.upload-logo') }}', { method: 'POST', body: fd });
const data = await res.json();
if (res.ok) {
this.logoUrl = data.url + '?t=' + Date.now();
} else {
this.errorMsg = data.message ?? 'Upload failed.';
}
} catch {
this.errorMsg = 'Network error. Please try again.';
}
this.uploading = false;
input.value = '';
},
async removeLogo() {
if (!confirm('Remove this logo?')) return;
const fd = new FormData();
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
fd.append('_method', 'DELETE');
await fetch('{{ route('invoice-settings.delete-logo') }}', { method: 'POST', body: fd });
this.logoUrl = '';
}
}">
{{-- Current logo preview --}}
<template x-if="logoUrl">
<div class="flex items-center gap-3 mb-3">
<img :src="logoUrl" alt="Logo"
class="h-16 w-16 object-cover rounded-2xl border border-pink-100 shrink-0">
<div class="flex-1 min-w-0">
<p class="text-xs font-semibold text-gray-600">Current Logo</p>
<p class="text-xs text-gray-400 mt-0.5">Choose a new file to replace it</p>
</div>
<button type="button" @click="removeLogo()"
class="text-xs px-3 py-1.5 rounded-full border border-red-200 text-red-400 hover:bg-red-50 shrink-0">
🗑️ Remove
</button>
</div>
</template>
<label class="block text-xs font-semibold text-gray-600 mb-1">Logo / Profile Picture</label>
<div class="relative">
<input type="file" accept="image/*" @change="pickFile($event.target)" class="w-full text-sm text-gray-600
file:mr-3 file:py-1.5 file:px-4
file:rounded-full file:border-0
file:text-xs file:font-semibold
file:bg-pink-50 file:text-pink-600
hover:file:bg-pink-100 cursor-pointer" :disabled="uploading">
<span x-show="uploading" class="text-xs text-pink-500 mt-1 block">Uploading…</span>
<span x-show="errorMsg" x-text="errorMsg" class="text-xs text-red-500 mt-1 block"></span>
</div>
<p class="text-xs text-gray-400 mt-1">Max 2 MB. Uploads immediately on file selection.</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Talent / Business Name</label>
<input type="text" name="header_name" value="{{ old('header_name', $settings->header_name) }}"
class="input-soft text-sm" placeholder="Your name or brand">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Title / Tagline</label>
<input type="text" name="header_title" value="{{ old('header_title', $settings->header_title) }}"
class="input-soft text-sm" placeholder="Content Creator · KOL">
</div>
<div x-data="{ color: '{{ old('accent_color', $settings->accent_color ?? '#FFB7C5') }}' }">
<label class="block text-xs font-semibold text-gray-600 mb-1">Accent Color</label>
<div class="flex items-center gap-3 mt-1">
<input type="color" name="accent_color" x-model="color"
class="w-10 h-10 rounded-xl cursor-pointer border-2 border-pink-100 p-0.5">
</div>
<p class="text-xs text-gray-400 mt-1.5">Applied to your invoice header name and totals.</p>
</div>
</div>
</div>
{{-- Invoice Defaults --}}
<div class="cat-card p-5">
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">📋 Invoice Defaults</h2>
<p class="text-xs text-gray-400 mb-4">These values will pre-fill every new invoice you create.</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Tax Rate (%)</label>
<input type="number" name="default_tax_rate" step="0.01" min="0" max="100"
value="{{ old('default_tax_rate', $settings->default_tax_rate ?? 11) }}"
class="input-soft text-sm" placeholder="11">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Currency</label>
<select name="default_currency" class="input-soft text-sm">
@foreach(['IDR' => 'IDR (Rupiah)', 'USD' => 'USD (Dollar)', 'SGD' => 'SGD', 'MYR' => 'MYR (Ringgit)'] as $code => $label)
<option value="{{ $code }}" {{ old('default_currency', $settings->default_currency ?? 'IDR') === $code ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>
<div class="sm:col-span-2">
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Service Description</label>
<input type="text" name="default_service_description"
value="{{ old('default_service_description', $settings->default_service_description) }}"
class="input-soft text-sm" placeholder="e.g. Instagram Feed Post — 1 carousel">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Bank Name</label>
<input type="text" name="default_bank_name"
value="{{ old('default_bank_name', $settings->default_bank_name) }}" class="input-soft text-sm"
placeholder="BCA, Mandiri…">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Branch Name</label>
<input type="text" name="default_branch_name"
value="{{ old('default_branch_name', $settings->default_branch_name) }}"
class="input-soft text-sm" placeholder="KCP Sudirman">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Account Number</label>
<input type="text" name="default_bank_account"
value="{{ old('default_bank_account', $settings->default_bank_account) }}"
class="input-soft text-sm" placeholder="1234567890">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Account Holder</label>
<input type="text" name="default_bank_holder"
value="{{ old('default_bank_holder', $settings->default_bank_holder) }}"
class="input-soft text-sm" placeholder="Your full name">
</div>
<div class="sm:col-span-2">
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Notes / Payment Terms</label>
<textarea name="default_notes" rows="2" class="input-soft text-sm resize-none"
placeholder="e.g. Payment due within 14 days from invoice date.">{{ old('default_notes', $settings->default_notes) }}</textarea>
</div>
</div>
</div>
<button type="submit" class="btn-sakura w-full py-3">💾 Save Settings</button>
</form>
</div>
@if(session('success'))
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)"
class="flex items-center justify-between gap-3 bg-green-50 border border-green-200 rounded-2xl px-4 py-3 text-sm text-green-700 mb-4">
<span>{{ session('success') }}</span>
<button @click="show = false" class="text-green-500 hover:text-green-700 text-lg leading-none">✕</button>
</div>
@endif
<form action="{{ route('invoice-settings.update') }}" method="POST" enctype="multipart/form-data"
class="space-y-5">
@csrf
{{-- Number Format --}}
<div class="cat-card p-5">
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">🔢 Invoice Number Format</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">
Prefix <span class="text-pink-400">*</span>
</label>
<input type="text" name="invoice_prefix"
value="{{ old('invoice_prefix', $settings->invoice_prefix) }}"
class="input-soft text-sm" placeholder="INV-EA" required
id="prefix-input" oninput="updatePreview()">
<p class="text-xs text-gray-400 mt-1">e.g. <code>INV-EA</code> → <strong>INV-EA-001</strong></p>
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">
Next Number <span class="text-pink-400">*</span>
</label>
<input type="number" name="invoice_next_number"
value="{{ old('invoice_next_number', $settings->invoice_next_number) }}"
class="input-soft text-sm" min="1" required
id="number-input" oninput="updatePreview()">
<p class="text-xs text-gray-400 mt-1">Used for the next new invoice</p>
</div>
</div>
<div class="mt-3 bg-pink-50 rounded-xl px-4 py-2.5 text-sm text-gray-600">
Preview: <strong class="text-pink-600" id="number-preview">
{{ rtrim(old('invoice_prefix', $settings->invoice_prefix), '-') }}-{{ str_pad((string) (old('invoice_next_number', $settings->invoice_next_number) ?? 1), 3, '0', STR_PAD_LEFT) }}
</strong>
</div>
</div>
{{-- Header / Branding --}}
<div class="cat-card p-5">
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">🎨 Invoice Header</h2>
{{-- Logo auto-upload (independent of main Save form) --}}
<div class="mb-4"
x-data="{
uploading: false,
errorMsg: '',
logoUrl: '{{ $settings->header_logo ? asset('storage/' . $settings->header_logo) : '' }}',
async pickFile(input) {
if (!input.files.length) return;
this.uploading = true;
this.errorMsg = '';
const fd = new FormData();
fd.append('header_logo', input.files[0]);
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
try {
const res = await fetch('{{ route('invoice-settings.upload-logo') }}', { method: 'POST', body: fd });
const data = await res.json();
if (res.ok) {
this.logoUrl = data.url + '?t=' + Date.now();
} else {
this.errorMsg = data.message ?? 'Upload failed.';
}
} catch {
this.errorMsg = 'Network error. Please try again.';
}
this.uploading = false;
input.value = '';
},
async removeLogo() {
if (!confirm('Remove this logo?')) return;
const fd = new FormData();
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
fd.append('_method', 'DELETE');
await fetch('{{ route('invoice-settings.delete-logo') }}', { method: 'POST', body: fd });
this.logoUrl = '';
}
}">
{{-- Current logo preview --}}
<template x-if="logoUrl">
<div class="flex items-center gap-3 mb-3">
<img :src="logoUrl" alt="Logo"
class="h-16 w-16 object-cover rounded-2xl border border-pink-100 shrink-0">
<div class="flex-1 min-w-0">
<p class="text-xs font-semibold text-gray-600">Current Logo</p>
<p class="text-xs text-gray-400 mt-0.5">Choose a new file to replace it</p>
</div>
<button type="button" @click="removeLogo()"
class="text-xs px-3 py-1.5 rounded-full border border-red-200 text-red-400 hover:bg-red-50 shrink-0">
🗑️ Remove
</button>
</div>
</template>
<label class="block text-xs font-semibold text-gray-600 mb-1">Logo / Profile Picture</label>
<div class="relative">
<input type="file" accept="image/*"
@change="pickFile($event.target)"
class="w-full text-sm text-gray-600
file:mr-3 file:py-1.5 file:px-4
file:rounded-full file:border-0
file:text-xs file:font-semibold
file:bg-pink-50 file:text-pink-600
hover:file:bg-pink-100 cursor-pointer"
:disabled="uploading">
<span x-show="uploading" class="text-xs text-pink-500 mt-1 block">Uploading…</span>
<span x-show="errorMsg" x-text="errorMsg" class="text-xs text-red-500 mt-1 block"></span>
</div>
<p class="text-xs text-gray-400 mt-1">Max 2 MB. Uploads immediately on file selection.</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Talent / Business Name</label>
<input type="text" name="header_name"
value="{{ old('header_name', $settings->header_name) }}"
class="input-soft text-sm" placeholder="Your name or brand">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Title / Tagline</label>
<input type="text" name="header_title"
value="{{ old('header_title', $settings->header_title) }}"
class="input-soft text-sm" placeholder="Content Creator · KOL">
</div>
<div x-data="{ color: '{{ old('accent_color', $settings->accent_color ?? '#FFB7C5') }}' }">
<label class="block text-xs font-semibold text-gray-600 mb-1">Accent Color</label>
<div class="flex items-center gap-3 mt-1">
<input type="color" name="accent_color" x-model="color"
class="w-10 h-10 rounded-xl cursor-pointer border-2 border-pink-100 p-0.5">
</div>
<p class="text-xs text-gray-400 mt-1.5">Applied to your invoice header name and totals.</p>
</div>
</div>
</div>
{{-- Invoice Defaults --}}
<div class="cat-card p-5">
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">📋 Invoice Defaults</h2>
<p class="text-xs text-gray-400 mb-4">These values will pre-fill every new invoice you create.</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Tax Rate (%)</label>
<input type="number" name="default_tax_rate" step="0.01" min="0" max="100"
value="{{ old('default_tax_rate', $settings->default_tax_rate ?? 11) }}"
class="input-soft text-sm" placeholder="11">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Currency</label>
<select name="default_currency" class="input-soft text-sm">
@foreach(['IDR' => 'IDR (Rupiah)', 'USD' => 'USD (Dollar)', 'SGD' => 'SGD', 'MYR' => 'MYR (Ringgit)'] as $code => $label)
<option value="{{ $code }}" {{ old('default_currency', $settings->default_currency ?? 'IDR') === $code ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>
<div class="sm:col-span-2">
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Service Description</label>
<input type="text" name="default_service_description"
value="{{ old('default_service_description', $settings->default_service_description) }}"
class="input-soft text-sm" placeholder="e.g. Instagram Feed Post — 1 carousel">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Bank Name</label>
<input type="text" name="default_bank_name"
value="{{ old('default_bank_name', $settings->default_bank_name) }}"
class="input-soft text-sm" placeholder="BCA, Mandiri…">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Branch Name</label>
<input type="text" name="default_branch_name"
value="{{ old('default_branch_name', $settings->default_branch_name) }}"
class="input-soft text-sm" placeholder="KCP Sudirman">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Account Number</label>
<input type="text" name="default_bank_account"
value="{{ old('default_bank_account', $settings->default_bank_account) }}"
class="input-soft text-sm" placeholder="1234567890">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Account Holder</label>
<input type="text" name="default_bank_holder"
value="{{ old('default_bank_holder', $settings->default_bank_holder) }}"
class="input-soft text-sm" placeholder="Your full name">
</div>
<div class="sm:col-span-2">
<label class="block text-xs font-semibold text-gray-600 mb-1">Default Notes / Payment Terms</label>
<textarea name="default_notes" rows="2" class="input-soft text-sm resize-none"
placeholder="e.g. Payment due within 14 days from invoice date.">{{ old('default_notes', $settings->default_notes) }}</textarea>
</div>
</div>
</div>
<button type="submit" class="btn-sakura w-full py-3">💾 Save Settings</button>
</form>
</div>
@push('scripts')
<script>
function updatePreview() {
const prefix = document.getElementById('prefix-input').value.replace(/-+$/, '') || 'INV';
const num = parseInt(document.getElementById('number-input').value) || 1;
document.getElementById('number-preview').textContent = prefix + '-' + String(num).padStart(3, '0');
}
</script>
@endpush
@endsection
@push('scripts')
<script>
function updatePreview() {
const prefix = document.getElementById('prefix-input').value.replace(/-+$/, '') || 'INV';
const num = parseInt(document.getElementById('number-input').value) || 1;
document.getElementById('number-preview').textContent = prefix + '-' + String(num).padStart(3, '0');
}
</script>
@endpush
@endsection

View File

@@ -5,136 +5,151 @@
@section('page-subtitle', $invoice->title)
@section('content')
<div class="max-w-3xl mx-auto">
<div class="flex gap-3 mb-6">
<a href="{{ route('invoices.edit', $invoice) }}" class="btn-sakura">Edit</a>
<a href="{{ route('invoices.pdf', $invoice) }}" target="_blank" class="btn btn-outline rounded-full text-pink-500 border-pink-300">📥 Download PDF</a>
<form action="{{ route('invoices.destroy', $invoice) }}" method="POST" onsubmit="return confirm('Delete this invoice?')">
@csrf @method('DELETE')
<button class="btn btn-ghost rounded-full text-error">Delete</button>
</form>
</div>
<div class="max-w-3xl mx-auto">
<div class="flex gap-3 mb-6">
<a href="{{ route('invoices.edit', $invoice) }}" class="btn-sakura">Edit</a>
<a href="{{ route('invoices.pdf', $invoice) }}" target="_blank"
class="btn-sakura text-sm px-4 py-2.5 rounded-full border border-pink-200 text-pink-500 hover:bg-pink-50">📥
Download PDF</a>
<form action="{{ route('invoices.destroy', $invoice) }}" method="POST"
onsubmit="return confirm('Delete this invoice?')">
@csrf @method('DELETE')
<button
class="btn-sakura text-sm px-4 py-2.5 rounded-full border border-red-200 text-red-500 hover:bg-red-50">Delete</button>
</form>
</div>
<div class="cat-card p-8">
{{-- Invoice Header --}}
<div class="flex justify-between items-start mb-8">
<div class="flex items-center gap-3">
@if($invoiceSettings->header_logo)
<img src="{{ asset('storage/' . $invoiceSettings->header_logo) }}"
alt="Logo" class="h-14 w-14 object-cover rounded-2xl border border-pink-100 shrink-0">
@endif
<div class="cat-card p-8">
{{-- Invoice Header --}}
<div class="flex justify-between items-start mb-8">
<div class="flex items-center gap-3">
@if($invoiceSettings->header_logo)
<img src="{{ asset('storage/' . $invoiceSettings->header_logo) }}" alt="Logo"
class="h-14 w-14 object-cover rounded-2xl border border-pink-100 shrink-0">
@endif
<div>
<div class="text-xl font-extrabold" style="color:{{ $invoiceSettings->accent_color ?? '#FFB7C5' }}">
{{ $invoiceSettings->header_name ?: '🐱 Cathy' }}
</div>
<div class="text-sm text-gray-400 mt-0.5">
{{ $invoiceSettings->header_title ?: 'Personal Talent Assistant' }}
</div>
</div>
</div>
<div class="text-right">
<div class="text-2xl font-bold text-gray-800">{{ $invoice->invoice_number }}</div>
<span class="badge {{ $invoice->status->badgeClass() }} mt-1">{{ $invoice->status->label() }}</span>
</div>
</div>
{{-- Billing Info --}}
<div class="grid grid-cols-2 gap-8 mb-8">
<div>
<div class="text-xl font-extrabold"
style="color:{{ $invoiceSettings->accent_color ?? '#FFB7C5' }}">
{{ $invoiceSettings->header_name ?: '🐱 Cathy' }}
</div>
<div class="text-sm text-gray-400 mt-0.5">
{{ $invoiceSettings->header_title ?: 'Personal Talent Assistant' }}
<h4 class="text-xs font-bold text-gray-400 uppercase mb-2">Bill To</h4>
@if($invoice->client)
<div class="font-semibold text-gray-800">{{ $invoice->client->name }}</div>
@if($invoice->client->company)
<div class="text-sm text-gray-500">{{ $invoice->client->company }}</div> @endif
@if($invoice->client->email)
<div class="text-sm text-gray-500">{{ $invoice->client->email }}</div> @endif
@if($invoice->client->full_address)
<div class="text-sm text-gray-400">{{ $invoice->client->full_address }}</div> @endif
@else
<div class="text-gray-500 text-sm">No client</div>
@endif
</div>
<div class="text-right">
<div class="text-xs font-bold text-gray-400 uppercase mb-2">Invoice Details</div>
<div class="text-sm space-y-1">
<div><span class="text-gray-400">Issue:</span> {{ $invoice->issue_date->format('d M Y') }}</div>
<div><span class="text-gray-400">Due:</span> <span
class="{{ $invoice->due_date->isPast() && $invoice->status->value !== 'paid' ? 'text-red-500 font-bold' : '' }}">{{ $invoice->due_date->format('d M Y') }}</span>
</div>
@if($invoice->paid_date)
<div><span class="text-gray-400">Paid:</span> {{ $invoice->paid_date->format('d M Y') }}</div>
@endif
</div>
</div>
</div>
<div class="text-right">
<div class="text-2xl font-bold text-gray-800">{{ $invoice->invoice_number }}</div>
<span class="badge {{ $invoice->status->badgeClass() }} mt-1">{{ $invoice->status->label() }}</span>
</div>
</div>
{{-- Billing Info --}}
<div class="grid grid-cols-2 gap-8 mb-8">
<div>
<h4 class="text-xs font-bold text-gray-400 uppercase mb-2">Bill To</h4>
@if($invoice->client)
<div class="font-semibold text-gray-800">{{ $invoice->client->name }}</div>
@if($invoice->client->company) <div class="text-sm text-gray-500">{{ $invoice->client->company }}</div> @endif
@if($invoice->client->email) <div class="text-sm text-gray-500">{{ $invoice->client->email }}</div> @endif
@if($invoice->client->full_address) <div class="text-sm text-gray-400">{{ $invoice->client->full_address }}</div> @endif
@else
<div class="text-gray-500 text-sm">No client</div>
@endif
</div>
<div class="text-right">
<div class="text-xs font-bold text-gray-400 uppercase mb-2">Invoice Details</div>
<div class="text-sm space-y-1">
<div><span class="text-gray-400">Issue:</span> {{ $invoice->issue_date->format('d M Y') }}</div>
<div><span class="text-gray-400">Due:</span> <span class="{{ $invoice->due_date->isPast() && $invoice->status->value !== 'paid' ? 'text-red-500 font-bold' : '' }}">{{ $invoice->due_date->format('d M Y') }}</span></div>
@if($invoice->paid_date) <div><span class="text-gray-400">Paid:</span> {{ $invoice->paid_date->format('d M Y') }}</div> @endif
</div>
</div>
</div>
{{-- Line Items --}}
@php
$accent = $invoiceSettings->accent_color ?? '#FFB7C5';
$hex = ltrim($accent, '#');
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
$tint = sprintf('#%02X%02X%02X',
(int)(255 * 0.92 + $r * 0.08),
(int)(255 * 0.92 + $g * 0.08),
(int)(255 * 0.92 + $b * 0.08)
);
@endphp
<table class="w-full mb-6">
<thead>
<tr class="text-xs text-gray-500 uppercase" style="background:{{ $tint }}">
<th class="text-left py-3 px-4 rounded-l-2xl">Description</th>
<th class="text-center py-3 px-4">Qty</th>
<th class="text-right py-3 px-4">Unit Price</th>
<th class="text-right py-3 px-4 rounded-r-2xl">Total</th>
</tr>
</thead>
<tbody>
@foreach($invoice->items as $item)
<tr class="border-b border-pink-50">
<td class="py-3 px-4 text-sm text-gray-700">
{{ $item->description }}
@if(!$item->is_taxable)
<span class="ml-1.5 text-xs bg-gray-100 text-gray-500 px-2 py-0.5 rounded-full font-medium">Non-Taxable</span>
@endif
</td>
<td class="py-3 px-4 text-sm text-center text-gray-600">{{ $item->quantity }}</td>
<td class="py-3 px-4 text-sm text-right text-gray-600">Rp {{ number_format($item->unit_price, 0, ',', '.') }}</td>
<td class="py-3 px-4 text-sm text-right font-semibold text-gray-800">Rp {{ number_format($item->total, 0, ',', '.') }}</td>
{{-- Line Items --}}
@php
$accent = $invoiceSettings->accent_color ?? '#FFB7C5';
$hex = ltrim($accent, '#');
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
$tint = sprintf(
'#%02X%02X%02X',
(int) (255 * 0.92 + $r * 0.08),
(int) (255 * 0.92 + $g * 0.08),
(int) (255 * 0.92 + $b * 0.08)
);
@endphp
<table class="w-full mb-6">
<thead>
<tr class="text-xs text-gray-500 uppercase" style="background:{{ $tint }}">
<th class="text-left py-3 px-4 rounded-l-2xl">Description</th>
<th class="text-center py-3 px-4">Qty</th>
<th class="text-right py-3 px-4">Unit Price</th>
<th class="text-right py-3 px-4 rounded-r-2xl">Total</th>
</tr>
@endforeach
</tbody>
</table>
</thead>
<tbody>
@foreach($invoice->items as $item)
<tr class="border-b border-pink-50">
<td class="py-3 px-4 text-sm text-gray-700">
{{ $item->description }}
@if(!$item->is_taxable)
<span
class="ml-1.5 text-xs bg-gray-100 text-gray-500 px-2 py-0.5 rounded-full font-medium">Non-Taxable</span>
@endif
</td>
<td class="py-3 px-4 text-sm text-center text-gray-600">{{ $item->quantity }}</td>
<td class="py-3 px-4 text-sm text-right text-gray-600">Rp
{{ number_format($item->unit_price, 0, ',', '.') }}</td>
<td class="py-3 px-4 text-sm text-right font-semibold text-gray-800">Rp
{{ number_format($item->total, 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
{{-- Totals --}}
<div class="flex justify-end mb-8">
<div class="w-72 space-y-2 text-sm">
<div class="flex justify-between text-gray-500">
<span>Subtotal</span><span class="font-medium">Rp {{ number_format($invoice->subtotal, 0, ',', '.') }}</span>
</div>
@if($invoice->tax_rate > 0)
<div class="flex justify-between text-amber-600">
<span>Tax ({{ $invoice->tax_rate }}% — paid by client)</span>
<span class="font-medium">Rp {{ number_format($invoice->tax_amount, 0, ',', '.') }}</span>
</div>
@endif
<div class="flex justify-between text-base font-bold pt-2 border-t border-pink-200"
style="color:{{ $invoiceSettings->accent_color ?? '#FFB7C5' }}">
<span>Grand Total</span><span>{{ $invoice->formatted_total }}</span>
{{-- Totals --}}
<div class="flex justify-end mb-8">
<div class="w-72 space-y-2 text-sm">
<div class="flex justify-between text-gray-500">
<span>Subtotal</span><span class="font-medium">Rp
{{ number_format($invoice->subtotal, 0, ',', '.') }}</span>
</div>
@if($invoice->tax_rate > 0)
<div class="flex justify-between text-amber-600">
<span>Tax ({{ $invoice->tax_rate }}% — paid by client)</span>
<span class="font-medium">Rp {{ number_format($invoice->tax_amount, 0, ',', '.') }}</span>
</div>
@endif
<div class="flex justify-between text-base font-bold pt-2 border-t border-pink-200"
style="color:{{ $invoiceSettings->accent_color ?? '#FFB7C5' }}">
<span>Grand Total</span><span>{{ $invoice->formatted_total }}</span>
</div>
</div>
</div>
{{-- Bank Info --}}
@if($invoice->bank_name)
<div class="rounded-2xl p-4 text-sm" style="background:{{ $tint }}">
<div class="font-semibold text-gray-700 mb-2">💳 Payment Info</div>
<div class="text-gray-600">
{{ $invoice->bank_name }}
@if($invoice->branch_name) · {{ $invoice->branch_name }} @endif
· {{ $invoice->bank_account }} · a/n {{ $invoice->bank_holder }}
</div>
</div>
@endif
@if($invoice->notes)
<div class="mt-4 text-sm text-gray-500 italic">{{ $invoice->notes }}</div>
@endif
</div>
{{-- Bank Info --}}
@if($invoice->bank_name)
<div class="rounded-2xl p-4 text-sm" style="background:{{ $tint }}">
<div class="font-semibold text-gray-700 mb-2">💳 Payment Info</div>
<div class="text-gray-600">
{{ $invoice->bank_name }}
@if($invoice->branch_name) · {{ $invoice->branch_name }} @endif
· {{ $invoice->bank_account }} · a/n {{ $invoice->bank_holder }}
</div>
</div>
@endif
@if($invoice->notes)
<div class="mt-4 text-sm text-gray-500 italic">{{ $invoice->notes }}</div>
@endif
</div>
</div>
@endsection
@endsection