- Increased icon sizes in service items, service orders, users, and technician management for better visibility. - Added custom loading indicators with appropriate icons in search fields for vehicles, work orders, and technicians. - Introduced invoice management routes for better organization and access control. - Created a new test for the estimate PDF functionality to ensure proper rendering and data integrity.
218 lines
12 KiB
PHP
218 lines
12 KiB
PHP
<div class="space-y-6">
|
||
<!-- Enhanced Header -->
|
||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||
<div>
|
||
<h1 class="text-3xl font-bold text-zinc-900 dark:text-white dark:text-white">Suppliers</h1>
|
||
<p class="mt-2 text-lg text-zinc-600 dark:text-zinc-400 dark:text-gray-300">
|
||
Manage {{ number_format($suppliers->total()) }} supplier relationships
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-col sm:flex-row gap-3">
|
||
<flux:button wire:navigate href="{{ route('inventory.purchase-orders.create') }}" variant="outline" icon="shopping-cart" size="sm">
|
||
New Purchase Order
|
||
</flux:button>
|
||
<flux:button wire:navigate href="{{ route('inventory.suppliers.create') }}" variant="primary" icon="plus" size="sm">
|
||
Add Supplier
|
||
</flux:button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Filters -->
|
||
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 border border-zinc-200 dark:border-zinc-700">
|
||
<div class="p-6">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<h3 class="text-lg font-semibold text-zinc-900 dark:text-white dark:text-white">Filter & Search</h3>
|
||
<flux:button wire:click="clearFilters" variant="outline" size="xs">
|
||
Clear All
|
||
</flux:button>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<!-- Enhanced Search -->
|
||
<div>
|
||
<flux:field>
|
||
<flux:label>Search Suppliers</flux:label>
|
||
<flux:input
|
||
wire:model.live.debounce.300ms="search"
|
||
placeholder="Name, email, or phone..."
|
||
icon="magnifying-glass"
|
||
/>
|
||
</flux:field>
|
||
</div>
|
||
|
||
<!-- Status Filter -->
|
||
<div>
|
||
<flux:field>
|
||
<flux:label>Status</flux:label>
|
||
<flux:select wire:model.live="statusFilter">
|
||
<option value="">All Suppliers</option>
|
||
<option value="1">✅ Active</option>
|
||
<option value="0">❌ Inactive</option>
|
||
</flux:select>
|
||
</flux:field>
|
||
</div>
|
||
|
||
<!-- Sort Options -->
|
||
<div>
|
||
<flux:field>
|
||
<flux:label>Sort By</flux:label>
|
||
<flux:select wire:model.live="sortBy">
|
||
<option value="name">Name A-Z</option>
|
||
<option value="created_at">Date Added</option>
|
||
<option value="contact_email">Email</option>
|
||
</flux:select>
|
||
</flux:field>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Active Filters Display -->
|
||
<div class="mt-4 flex flex-wrap gap-2">
|
||
@if($search)
|
||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
|
||
Search: "{{ $search }}"
|
||
<button wire:click="$set('search', '')" class="ml-2 text-blue-600 hover:text-blue-800">×</button>
|
||
</span>
|
||
@endif
|
||
@if($statusFilter !== '')
|
||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
||
Status: {{ $statusFilter ? 'Active' : 'Inactive' }}
|
||
<button wire:click="$set('statusFilter', '')" class="ml-2 text-green-600 hover:text-green-800">×</button>
|
||
</span>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Results Summary -->
|
||
<div class="flex items-center justify-between text-sm text-zinc-600 dark:text-zinc-400 dark:text-gray-400">
|
||
<div>
|
||
Showing {{ $suppliers->firstItem() ?? 0 }} to {{ $suppliers->lastItem() ?? 0 }} of {{ number_format($suppliers->total()) }} suppliers
|
||
</div>
|
||
<div class="flex items-center space-x-2">
|
||
<span>Per page:</span>
|
||
<flux:select wire:model.live="perPage" class="w-20">
|
||
<option value="10">10</option>
|
||
<option value="25">25</option>
|
||
<option value="50">50</option>
|
||
</flux:select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Suppliers Table -->
|
||
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 overflow-hidden">
|
||
<div class="overflow-x-auto">
|
||
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-700">
|
||
<thead class="bg-zinc-50 dark:bg-zinc-900">
|
||
<tr>
|
||
<th wire:click="sortBy('name')" class="px-6 py-3 text-left text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800">
|
||
<div class="flex items-center space-x-1">
|
||
<span>Name</span>
|
||
@if($sortBy === 'name')
|
||
<flux:icon.chevron-up class="w-6 h-6 {{ $sortDirection === 'asc' ? '' : 'rotate-180' }}" />
|
||
@endif
|
||
</div>
|
||
</th>
|
||
<th wire:click="sortBy('company_name')" class="px-6 py-3 text-left text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800">
|
||
<div class="flex items-center space-x-1">
|
||
<span>Company</span>
|
||
@if($sortBy === 'company_name')
|
||
<flux:icon.chevron-up class="w-6 h-6 {{ $sortDirection === 'asc' ? '' : 'rotate-180' }}" />
|
||
@endif
|
||
</div>
|
||
</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider">Contact</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider">Parts</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider">Rating</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider">Status</th>
|
||
<th class="px-6 py-3 text-right text-xs font-medium text-zinc-500 dark:text-zinc-400 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="bg-white dark:bg-zinc-800 divide-y divide-zinc-200 dark:divide-zinc-700">
|
||
@forelse($suppliers as $supplier)
|
||
<tr class="hover:bg-zinc-50 dark:hover:bg-zinc-700">
|
||
<td class="px-6 py-4 whitespace-nowrap">
|
||
<div>
|
||
<div class="text-sm font-medium text-zinc-900 dark:text-white dark:text-white">{{ $supplier->name }}</div>
|
||
@if($supplier->contact_person && $supplier->contact_person !== $supplier->name)
|
||
<div class="text-sm text-zinc-500 dark:text-zinc-400 dark:text-gray-400">Contact: {{ $supplier->contact_person }}</div>
|
||
@endif
|
||
</div>
|
||
</td>
|
||
<td class="px-6 py-4 whitespace-nowrap">
|
||
<div class="text-sm text-zinc-900 dark:text-white dark:text-white">
|
||
{{ $supplier->company_name ?: 'N/A' }}
|
||
</div>
|
||
@if($supplier->city && $supplier->state)
|
||
<div class="text-sm text-zinc-500 dark:text-zinc-400 dark:text-gray-400">
|
||
{{ $supplier->city }}, {{ $supplier->state }}
|
||
</div>
|
||
@endif
|
||
</td>
|
||
<td class="px-6 py-4 whitespace-nowrap">
|
||
<div class="text-sm text-zinc-900 dark:text-white dark:text-white">
|
||
@if($supplier->email)
|
||
<div>{{ $supplier->email }}</div>
|
||
@endif
|
||
@if($supplier->phone)
|
||
<div>{{ $supplier->phone }}</div>
|
||
@endif
|
||
</div>
|
||
</td>
|
||
<td class="px-6 py-4 whitespace-nowrap text-sm text-zinc-900 dark:text-white dark:text-white">
|
||
<flux:badge size="sm" color="blue">{{ $supplier->parts_count }} parts</flux:badge>
|
||
</td>
|
||
<td class="px-6 py-4 whitespace-nowrap text-sm text-zinc-900 dark:text-white dark:text-white">
|
||
@if($supplier->rating)
|
||
<div class="flex items-center">
|
||
<div class="flex items-center">
|
||
@for($i = 1; $i <= 5; $i++)
|
||
@if($i <= $supplier->rating)
|
||
<flux:icon.star class="w-6 h-6 text-yellow-400" />
|
||
@else
|
||
<flux:icon.star class="w-6 h-6 text-gray-300 dark:text-zinc-600 dark:text-zinc-400" />
|
||
@endif
|
||
@endfor
|
||
</div>
|
||
<span class="ml-1 text-sm">{{ number_format($supplier->rating, 1) }}</span>
|
||
</div>
|
||
@else
|
||
<span class="text-gray-400">No rating</span>
|
||
@endif
|
||
</td>
|
||
<td class="px-6 py-4 whitespace-nowrap">
|
||
@if($supplier->is_active)
|
||
<flux:badge size="sm" color="green">Active</flux:badge>
|
||
@else
|
||
<flux:badge size="sm" color="red">Inactive</flux:badge>
|
||
@endif
|
||
</td>
|
||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
|
||
<flux:button wire:navigate href="{{ route('inventory.suppliers.edit', $supplier) }}" size="sm" variant="subtle">
|
||
Edit
|
||
</flux:button>
|
||
</td>
|
||
</tr>
|
||
@empty
|
||
<tr>
|
||
<td colspan="7" class="px-6 py-12 text-center">
|
||
<div class="text-zinc-500 dark:text-zinc-400 dark:text-gray-400">
|
||
<flux:icon.building-office class="mx-auto h-12 w-12 mb-4 opacity-40" />
|
||
<p class="text-lg font-medium">No suppliers found</p>
|
||
<p class="text-sm">Get started by adding your first supplier.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Pagination -->
|
||
@if($suppliers->hasPages())
|
||
<div class="bg-zinc-50 dark:bg-zinc-900 px-6 py-3 border-t border-zinc-200 dark:border-zinc-700">
|
||
{{ $suppliers->links() }}
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|