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:
@@ -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' : '' }}>
|
||||
↳ {{ $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' : '' }}>
|
||||
↳ {{ $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>
|
||||
|
||||
Reference in New Issue
Block a user