Add contract management and invoice settings pages

- Created a new Blade view for managing brand contracts with forms for adding and editing contracts, including client and category selection.
- Implemented success message notifications for contract actions.
- Added a new Blade view for invoice settings, allowing customization of invoice number format, header branding, and default values for new invoices.
- Introduced a new PDF view for generating rate cards with customizable styles based on user profile settings.
- Developed a profile management page for rate cards, enabling users to update their identity, appearance, and visibility settings.
This commit is contained in:
I-Fun
2026-04-01 21:47:34 +07:00
parent d3c35ad730
commit 22f03ccca7
48 changed files with 2415 additions and 296 deletions

View File

@@ -6,70 +6,77 @@
@section('content')
{{-- ── Filters bar ─────────────────────────────────────────── --}}
<div class="flex flex-col gap-3 mb-5">
<div class="flex flex-col sm:flex-row sm:items-center gap-3">
{{-- Local text filter --}}
<div class="relative flex-1 max-w-xs">
<span class="absolute left-3 top-2.5 text-gray-400 text-sm">🔍</span>
<input type="text" placeholder="Filter by name…" class="input-soft pl-9 text-sm"
{{-- ── Toolbar + collapsible filter ───────────────────────── --}}
<div x-data="{ open: {{ request('category') || request('status') ? 'true' : 'false' }} }">
<div class="flex items-center justify-between gap-2 mb-3">
<div class="flex items-center gap-2">
<a href="{{ route('clients.create') }}" class="btn-sakura text-sm">+ Add</a>
@if(request('category') || request('status'))
<a href="{{ route('clients.index') }}"
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">
✕ Clear filters
</a>
@endif
</div>
<button @click="open = !open"
class="flex items-center gap-1.5 text-xs px-3 py-2 rounded-full border transition-colors"
:class="open ? 'border-pink-300 bg-pink-50 text-pink-600' : 'border-gray-200 text-gray-500 hover:bg-gray-50'">
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 4h18M7 10h10M11 16h2"/>
</svg>
Filter
@if(request('category') || request('status'))
<span class="w-2 h-2 rounded-full bg-pink-400 inline-block ml-0.5"></span>
@endif
</button>
</div>
{{-- ── Filters panel (collapsible) ────────────────────────── --}}
<div x-show="open" x-transition.duration.150ms>
<form method="GET" action="{{ route('clients.index') }}" id="filter-form" class="cat-card p-3 mb-4">
{{-- Row 1: search + dropdowns --}}
<div class="flex flex-col sm:flex-row sm:items-center gap-2">
{{-- Search (fills remaining space on desktop) --}}
<div class="relative flex-1 min-w-0">
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-sm pointer-events-none">🔍</span>
<input type="text" placeholder="Filter by name…" class="input-soft pl-9 text-sm py-2 w-full"
oninput="filterClients(this.value)" id="search-input">
</div>
{{-- Category filter --}}
<form method="GET" action="{{ route('clients.index') }}" class="flex flex-wrap gap-2 items-center" id="filter-form">
<select name="category" class="input-soft text-sm py-2"
onchange="document.getElementById('filter-form').submit()">
<option value="">All Categories</option>
@foreach($categories as $parent)
<option value="{{ $parent->id }}"
{{ request('category') == $parent->id ? 'selected' : '' }}>
{{ $parent->icon }} {{ $parent->name }}
</option>
@foreach($parent->children as $child)
<option value="{{ $child->id }}"
{{ request('category') == $child->id ? 'selected' : '' }}>
&nbsp;&nbsp;↳ {{ $child->icon }} {{ $child->name }}
</option>
@endforeach
@endforeach
</select>
<select name="status" class="input-soft text-sm py-2"
onchange="document.getElementById('filter-form').submit()">
<option value="">All Statuses</option>
@foreach($statuses as $s)
<option value="{{ $s->value }}" {{ request('status') === $s->value ? 'selected' : '' }}>
{{ $s->label() }}
{{-- Category dropdown --}}
<select name="category" class="input-soft text-sm py-2 sm:w-48 shrink-0"
onchange="this.form.submit()">
<option value="">All Categories</option>
@foreach($categories as $parent)
<option value="{{ $parent->id }}"
{{ request('category') == $parent->id ? 'selected' : '' }}>
{{ $parent->icon }} {{ $parent->name }}
</option>
@foreach($parent->children as $child)
<option value="{{ $child->id }}"
{{ request('category') == $child->id ? 'selected' : '' }}>
&nbsp;&nbsp;↳ {{ $child->name }}
</option>
@endforeach
</select>
@endforeach
</select>
@if(request('category') || request('status'))
<a href="{{ route('clients.index') }}" class="text-xs text-gray-400 hover:text-gray-600">✕ Clear</a>
@endif
</form>
<a href="{{ route('clients.create') }}" class="btn-sakura text-sm text-center shrink-0">+ Add Client</a>
{{-- Status dropdown --}}
<select name="status" class="input-soft text-sm py-2 sm:w-36 shrink-0"
onchange="this.form.submit()">
<option value="">All Statuses</option>
@foreach($statuses as $s)
<option value="{{ $s->value }}" {{ request('status') === $s->value ? 'selected' : '' }}>
{{ $s->label() }}
</option>
@endforeach
</select>
</div>
{{-- Category pills (top-level only) --}}
<div class="flex gap-2 overflow-x-auto pb-1 -mx-4 px-4 lg:mx-0 lg:px-0">
<a href="{{ route('clients.index') }}"
class="shrink-0 text-xs px-3 py-1.5 rounded-full font-semibold transition-colors
{{ ! request('category') ? 'bg-pink-400 text-white' : 'bg-white border border-pink-200 text-gray-600 hover:bg-pink-50' }}">
All
</a>
@foreach($categories as $cat)
<a href="{{ route('clients.index', ['category' => $cat->id, 'status' => request('status')]) }}"
class="shrink-0 text-xs px-3 py-1.5 rounded-full font-semibold transition-colors whitespace-nowrap
{{ request('category') == $cat->id ? 'bg-pink-400 text-white' : 'bg-white border border-pink-200 text-gray-600 hover:bg-pink-50' }}"
style="{{ request('category') == $cat->id ? '' : 'border-color:' . $cat->color }}">
{{ $cat->icon }} {{ $cat->name }}
</a>
@endforeach
</div>
</form>
</div>
</div>{{-- end x-data --}}
{{-- ── Client grid ─────────────────────────────────────────── --}}
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4" id="clients-grid">
@@ -135,11 +142,12 @@
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 --}}
<form action="{{ route('clients.destroy', $client) }}" method="POST"
onsubmit="return confirm('Remove this client?')">
@csrf @method('DELETE')
<button class="text-xs py-1.5 px-3 rounded-full border border-red-100 text-red-400 hover:bg-red-50">
Del
✕
</button>
</form>
</div>