[FIX] 取貨碼操作紀錄顯示異常修正

1. 修正 index.blade.php 的 HTML 標籤嵌套錯誤,解決操作紀錄分頁空白問題。
2. 移除暫時的除錯訊息。
3. 整合 SystemOperationLog 機制於取貨碼模組。
This commit is contained in:
sky121113 2026-04-30 10:28:04 +08:00
parent 84bf7de750
commit 820e1c87db
6 changed files with 376 additions and 81 deletions

View File

@ -10,6 +10,8 @@ use App\Models\Machine\Machine;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use App\Models\System\SystemOperationLog;
class SalesController extends Controller
{
// 銷售&金流紀錄
@ -30,34 +32,58 @@ class SalesController extends Controller
// 取貨碼設定
public function pickupCodes(Request $request)
{
$query = PickupCode::with(['machine.slots.product', 'creator'])->latest();
$tab = $request->input('tab', 'list');
$isAjax = $request->ajax();
if ($request->search) {
$query->where(function ($q) use ($request) {
$q->where('code', 'like', "%{$request->search}%")
->orWhereHas('machine', function ($mq) use ($request) {
$mq->where('name', 'like', "%{$request->search}%")
->orWhere('serial_no', 'like', "%{$request->search}%");
});
});
}
if ($request->status && trim($request->status) !== '') {
$query->where('status', trim($request->status));
}
$per_page = $request->input('per_page', 15);
$pickupCodes = $query->paginate($per_page)->withQueryString();
// 供新增彈窗使用的機台清單
$machines = Machine::all();
return view('admin.sales.pickup-codes.index', [
$data = [
'title' => '取貨碼設定',
'description' => '產生與管理商品取貨驗證碼',
'pickupCodes' => $pickupCodes,
'machines' => $machines,
]);
'tab' => $tab,
];
// 1. 取貨碼列表 (list)
if (!$isAjax || $tab === 'list') {
$query = PickupCode::with(['machine.slots.product', 'creator'])->latest();
if ($request->search) {
$query->where(function ($q) use ($request) {
$q->where('code', 'like', "%{$request->search}%")
->orWhereHas('machine', function ($mq) use ($request) {
$mq->where('name', 'like', "%{$request->search}%")
->orWhere('serial_no', 'like', "%{$request->search}%");
});
});
}
if ($request->status && trim($request->status) !== '') {
$query->where('status', trim($request->status));
}
$per_page = $request->input('per_page', 15);
$data['pickupCodes'] = $query->paginate($per_page, ['*'], 'list_page')->withQueryString();
// 供新增彈窗使用的機台清單
$data['machines'] = Machine::all();
}
// 2. 操作紀錄 (logs)
if (!$isAjax || $tab === 'logs') {
$data['logs'] = SystemOperationLog::with('user:id,name')
->where('module', 'pickup_code')
->latest()
->paginate($request->input('per_page', 15), ['*'], 'log_page')
->withQueryString();
}
if ($isAjax) {
return response()->json([
'success' => true,
'tab' => $tab,
'html' => view('admin.sales.pickup-codes.partials.tab-' . $tab, $data)->render()
]);
}
return view('admin.sales.pickup-codes.index', $data);
}
/**
@ -85,6 +111,16 @@ class SalesController extends Controller
'created_by' => Auth::id(),
]);
SystemOperationLog::create([
'company_id' => $machine->company_id,
'user_id' => Auth::id(),
'module' => 'pickup_code',
'action' => 'create',
'target_id' => $pickupCode->id,
'target_type' => PickupCode::class,
'new_values' => $pickupCode->toArray(),
]);
return back()->with('success', __('Pickup code generated: :code', ['code' => $pickupCode->code]));
}
@ -97,10 +133,23 @@ class SalesController extends Controller
'expires_at' => 'required|date|after:now',
]);
$oldValues = $pickupCode->toArray();
$pickupCode->update([
'expires_at' => $validated['expires_at'],
]);
SystemOperationLog::create([
'company_id' => $pickupCode->company_id,
'user_id' => Auth::id(),
'module' => 'pickup_code',
'action' => 'update',
'target_id' => $pickupCode->id,
'target_type' => PickupCode::class,
'old_values' => $oldValues,
'new_values' => $pickupCode->toArray(),
]);
return back()->with('success', __('Pickup code updated.'));
}
@ -109,7 +158,20 @@ class SalesController extends Controller
*/
public function destroyPickupCode(PickupCode $pickupCode)
{
$oldValues = $pickupCode->toArray();
$pickupCode->update(['status' => 'cancelled']);
SystemOperationLog::create([
'company_id' => $pickupCode->company_id,
'user_id' => Auth::id(),
'module' => 'pickup_code',
'action' => 'cancel',
'target_id' => $pickupCode->id,
'target_type' => PickupCode::class,
'old_values' => $oldValues,
'new_values' => $pickupCode->toArray(),
]);
return back()->with('success', __('Pickup code cancelled.'));
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\Models\System;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Traits\TenantScoped;
use App\Models\User;
class SystemOperationLog extends Model
{
use HasFactory, TenantScoped;
protected $fillable = [
'company_id',
'user_id',
'module',
'action',
'target_id',
'target_type',
'old_values',
'new_values',
'note',
'ip_address',
'user_agent',
];
protected $casts = [
'old_values' => 'array',
'new_values' => 'array',
];
public function user()
{
return $this->belongsTo(User::class);
}
}

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('system_operation_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('company_id')->nullable()->constrained()->onDelete('cascade');
$table->foreignId('user_id')->nullable()->constrained()->onDelete('set null');
$table->string('module', 50)->index();
$table->string('action', 50);
$table->unsignedBigInteger('target_id')->nullable()->index();
$table->string('target_type', 100)->nullable();
$table->json('old_values')->nullable();
$table->json('new_values')->nullable();
$table->text('note')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('system_operation_logs');
}
};

View File

@ -6,30 +6,13 @@ $slotSelectConfig = [
"hasSearch" => true,
"searchPlaceholder" => __("Search Slot..."),
"isHidePlaceholder" => false,
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg
focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200
placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10",
"toggleClasses" => "hs-select-toggle luxury-select-toggle",
"toggleTemplate" => '<button type="button" aria-expanded="false"><span class="me-2" data-icon></span><span
class="text-slate-800 dark:text-slate-200" data-title></span>
<div class="ms-auto"><svg class="size-4 text-slate-400 transition-transform duration-300"
xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="m6 9 6 6 6-6" />
</svg></div>
</button>',
"dropdownClasses" => "hs-select-menu w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10
rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[150] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer
hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between
transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span
class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg"
width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg></span></div>'
"toggleTemplate" => '<button type="button" aria-expanded="false"><span class="me-2" data-icon></span><span class="text-slate-800 dark:text-slate-200" data-title></span><div class="ms-auto"><svg class="size-4 text-slate-400 transition-transform duration-300" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></svg></div></button>',
"dropdownClasses" => "hs-select-menu w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[150] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span></div>'
];
@endphp
@ -50,18 +33,33 @@ transition-all duration-300",
</button>
</x-page-header>
{{-- Main Content Card --}}
<div class="luxury-card rounded-3xl p-8 animate-luxury-in relative overflow-hidden">
<x-luxury-spinner show="isLoadingTable" />
{{-- Tab Navigation --}}
<div class="mb-4">
<x-tab-nav model="activeTab">
<x-tab-nav-item value="list" :label="__('Pickup Codes')" model="activeTab" />
<x-tab-nav-item value="logs" :label="__('Operation Records')" model="activeTab" />
</x-tab-nav>
</div>
<div id="ajax-content-container"
:class="{ 'opacity-30 pointer-events-none transition-opacity duration-300': isLoadingTable }"
@ajax:navigate.prevent.stop="fetchPage($event.detail.url)"
@click="if ($event.target.closest('a') && $event.target.closest('a').href && ($event.target.closest('a').href.includes('page=') || $event.target.closest('a').href.includes('per_page='))) { $event.preventDefault(); fetchPage($event.target.closest('a').href); }">
{{-- Content Containers --}}
<div class="relative min-h-[400px]">
{{-- List Tab --}}
<div x-show="activeTab === 'list'" x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4" x-transition:enter-end="opacity-100 translate-y-0"
style="display: none;">
<form action="{{ route('admin.sales.pickup-codes') }}" method="GET"
class="flex flex-col md:flex-row md:items-center gap-3 mb-8"
@submit.prevent="fetchPage($el.action + '?' + new URLSearchParams(new FormData($el)).toString())">
{{-- Main Content Card --}}
<div class="luxury-card rounded-3xl p-8 animate-luxury-in relative overflow-hidden">
<x-luxury-spinner show="isLoadingTable" />
<div id="tab-list-container"
:class="{ 'opacity-30 pointer-events-none transition-opacity duration-300': isLoadingTable }"
@ajax:navigate.prevent.stop="fetchPage($event.detail.url)"
@click="if ($event.target.closest('a') && $event.target.closest('a').href && ($event.target.closest('a').href.includes('page=') || $event.target.closest('a').href.includes('per_page='))) { $event.preventDefault(); fetchPage($event.target.closest('a').href); }">
<form action="{{ route('admin.sales.pickup-codes') }}" method="GET"
class="flex flex-col md:flex-row md:items-center gap-3 mb-8"
@submit.prevent="fetchPage($el.action + '?' + new URLSearchParams(new FormData($el)).toString())">
<div class="relative group flex-1 md:flex-none">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10">
@ -351,7 +349,26 @@ transition-all duration-300",
<div class="mt-8 border-t border-slate-100/50 dark:border-slate-800/50 pt-6">
{{ $pickupCodes->links('vendor.pagination.luxury') }}
</div>
</div> {{-- End tab-list-container --}}
</div> {{-- End luxury-card --}}
</div>
{{-- End List Tab --}}
{{-- Logs Tab --}}
<div x-show="activeTab === 'logs'" class="relative"
x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 translate-y-4"
x-transition:enter-end="opacity-100 translate-y-0" style="display: none;">
{{-- Main Content Card --}}
<div class="luxury-card rounded-3xl p-8 animate-luxury-in relative overflow-hidden">
<x-luxury-spinner show="tabLoading === 'logs'" />
<div id="tab-logs-container" class="relative">
@include('admin.sales.pickup-codes.partials.tab-logs')
</div>
</div>
</div>
{{-- End Logs Tab --}}
</div>
{{-- Create Modal --}}
@ -582,6 +599,8 @@ transition-all duration-300",
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('pickupCodeManager', (config) => ({
activeTab: '{{ request("tab", "list") }}',
tabLoading: null,
showCreateModal: false,
selectedMachine: '',
selectedSlot: '',
@ -598,6 +617,48 @@ transition-all duration-300",
slotSelectConfig: config,
customCode: '',
async fetchTabData(tab, containerSelector) {
if (this.tabLoading === tab) return;
this.tabLoading = tab;
const container = document.querySelector(containerSelector);
let url = '{{ route("admin.sales.pickup-codes") }}';
const urlObj = new URL(url, window.location.origin);
urlObj.searchParams.set('tab', tab);
urlObj.searchParams.set('_ajax', '1');
url = urlObj.toString();
try {
const response = await fetch(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json'
}
});
const data = await response.json();
if (data.success) {
if (container) {
container.innerHTML = data.html;
this.$nextTick(() => {
if (window.HSStaticMethods) window.HSStaticMethods.autoInit();
});
}
const historyUrl = new URL(url, window.location.origin);
historyUrl.searchParams.delete('_ajax');
window.history.pushState({}, '', historyUrl.toString());
}
} catch (e) {
console.error(e);
window.showToast?.('{{ __("Loading failed") }}', 'error');
} finally {
this.tabLoading = null;
}
},
generateRandomCode() {
this.customCode = Math.floor(Math.random() * 90000000 + 10000000).toString();
},
@ -668,14 +729,14 @@ transition-all duration-300",
copyQrLink() {
if (!this.activeTicketUrl) return;
navigator.clipboard.writeText(this.activeTicketUrl).then(() => {
window.showToast('{{ __('Link Copied') }}', 'success');
window.showToast("{{ __('Link Copied') }}", 'success');
});
},
copyQrCode() {
if (!this.activeQrCode) return;
navigator.clipboard.writeText(this.activeQrCode).then(() => {
window.showToast('{{ __('Code Copied') }}', 'success');
window.showToast("{{ __('Code Copied') }}", 'success');
});
},
@ -773,6 +834,22 @@ transition-all duration-300",
init() {
this.generateRandomCode();
// Tab 切換時更新 URL同庫存管理模式
this.$watch('activeTab', (newTab) => {
const url = new URL(window.location.origin + window.location.pathname);
url.searchParams.set('tab', newTab);
window.history.pushState({}, '', url);
if (newTab === 'logs') {
this.fetchTabData('logs', '#tab-logs-container');
}
this.$nextTick(() => {
if (window.HSStaticMethods) window.HSStaticMethods.autoInit();
});
});
this.$watch('slots', () => this.updateSlotSelect());
this.$watch('selectedMachine', (val) => {
this.syncSelect('modal-pickup-machine', val);

View File

@ -0,0 +1,98 @@
<div class="overflow-hidden bg-white/80 dark:bg-slate-900/80 backdrop-blur-xl border border-slate-200 dark:border-slate-800 shadow-sm rounded-xl">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 dark:divide-slate-800">
<thead class="bg-slate-50/50 dark:bg-slate-800/50">
<tr>
<th scope="col" class="px-6 py-4 text-left text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{{ __('Time') }}
</th>
<th scope="col" class="px-6 py-4 text-left text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{{ __('Operator') }}
</th>
<th scope="col" class="px-6 py-4 text-left text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{{ __('Actions') }}
</th>
<th scope="col" class="px-6 py-4 text-left text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{{ __('Target') }}
</th>
<th scope="col" class="px-6 py-4 text-left text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{{ __('Notes') }}
</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-800">
@forelse($logs as $log)
<tr class="hover:bg-slate-50/50 dark:hover:bg-slate-800/50 transition-colors duration-200">
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-600 dark:text-slate-300">
{{ $log->created_at->format('Y-m-d H:i:s') }}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center gap-2">
<div class="w-6 h-6 rounded-full bg-indigo-100 dark:bg-indigo-900/50 flex items-center justify-center text-indigo-600 dark:text-indigo-400 text-xs font-medium">
{{ Str::substr($log->user->name ?? 'Sys', 0, 1) }}
</div>
<span class="text-sm font-medium text-slate-700 dark:text-slate-200">
{{ $log->user->name ?? __('System') }}
</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if($log->action === 'create')
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800/50">
{{ __('Generate') }}
</span>
@elseif($log->action === 'update')
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-blue-50 text-blue-700 dark:bg-blue-500/10 dark:text-blue-400 border border-blue-200 dark:border-blue-800/50">
{{ __('Update') }}
</span>
@elseif($log->action === 'cancel')
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-400 border border-rose-200 dark:border-rose-800/50">
{{ __('Cancel') }}
</span>
@else
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-slate-50 text-slate-700 dark:bg-slate-500/10 dark:text-slate-400 border border-slate-200 dark:border-slate-800/50">
{{ $log->action }}
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-600 dark:text-slate-300">
ID: {{ $log->target_id }}
@if($log->new_values && isset($log->new_values['code']))
<span class="ml-2 px-2 py-0.5 rounded text-xs bg-slate-100 dark:bg-slate-800 font-mono">
{{ $log->new_values['code'] }}
</span>
@endif
</td>
<td class="px-6 py-4 text-sm text-slate-500 dark:text-slate-400">
@if($log->action === 'update' && isset($log->old_values['expires_at']) && isset($log->new_values['expires_at']))
<div>{{ __('Expires At') }}:
<span class="line-through opacity-70">{{ \Carbon\Carbon::parse($log->old_values['expires_at'])->format('Y-m-d H:i') }}</span>
<svg class="inline w-3 h-3 mx-1 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" /></svg>
<span class="text-amber-600 dark:text-amber-400 font-medium">{{ \Carbon\Carbon::parse($log->new_values['expires_at'])->format('Y-m-d H:i') }}</span>
</div>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-6 py-12 text-center">
<div class="flex flex-col items-center justify-center text-slate-400 dark:text-slate-500">
<svg class="w-12 h-12 mb-4 opacity-50" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
</svg>
<p class="text-sm font-medium">{{ __('No records found') }}</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- Pagination -->
@if(isset($logs) && $logs instanceof \Illuminate\Pagination\LengthAwarePaginator && $logs->hasPages())
<div class="px-6 py-4 border-t border-slate-200 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-800/50">
{{ $logs->links('vendor.pagination.luxury') }}
</div>
@endif
</div>

View File

@ -19,30 +19,13 @@ $config = [
"hasSearch" => (bool)$hasSearch,
"searchPlaceholder" => $placeholder ?: __('Search...'),
"isHidePlaceholder" => false,
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg
focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200
placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10",
"toggleClasses" => "hs-select-toggle luxury-select-toggle",
"toggleTemplate" => '<button type="button" aria-expanded="false"><span class="me-2" data-icon></span><span
class="text-slate-800 dark:text-slate-200" data-title></span>
<div class="ms-auto"><svg class="size-4 text-slate-400 transition-transform duration-300"
xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="m6 9 6 6 6-6" />
</svg></div>
</button>',
"dropdownClasses" => "hs-select-menu w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10
rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[150] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer
hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between
transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span
class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg"
width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg></span></div>'
"toggleTemplate" => '<button type="button" aria-expanded="false"><span class="me-2" data-icon></span><span class="text-slate-800 dark:text-slate-200" data-title></span><div class="ms-auto"><svg class="size-4 text-slate-400 transition-transform duration-300" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></svg></div></button>',
"dropdownClasses" => "hs-select-menu w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[150] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span></div>'
];
@endphp