diff --git a/frontend/src/hold-history/App.vue b/frontend/src/hold-history/App.vue index 9fa76b1..954e119 100644 --- a/frontend/src/hold-history/App.vue +++ b/frontend/src/hold-history/App.vue @@ -352,34 +352,34 @@ const holdTypeLabel = computed(() => { // ---- Event handlers ---- -function handleFilterChange(next) { +function handleApply(next) { const nextStartDate = next?.startDate || ''; const nextEndDate = next?.endDate || ''; - const nextHoldType = next?.holdType || 'quality'; - - const dateChanged = filterBar.startDate !== nextStartDate || filterBar.endDate !== nextEndDate; - const holdTypeChanged = filterBar.holdType !== nextHoldType; - - if (!dateChanged && !holdTypeChanged) { - return; - } filterBar.startDate = nextStartDate; filterBar.endDate = nextEndDate; - filterBar.holdType = nextHoldType; reasonFilter.value = ''; durationFilter.value = ''; recordType.value = ['new']; page.value = 1; updateUrlState(); - if (dateChanged) { - // Date changed -> new primary query (Oracle) - void executePrimaryQuery(); - } else { - // Only hold_type changed -> view refresh (cache only) - void refreshView(); - } + void executePrimaryQuery(); +} + +function handleHoldTypeChange(nextHoldType) { + const holdType = nextHoldType || 'quality'; + if (filterBar.holdType === holdType) return; + + filterBar.holdType = holdType; + reasonFilter.value = ''; + durationFilter.value = ''; + recordType.value = ['new']; + page.value = 1; + updateUrlState(); + + // Hold type only → view refresh (cache read, no Oracle query) + void refreshView(); } function handleRecordTypeChange() { @@ -502,7 +502,8 @@ onMounted(() => { :end-date="filterBar.endDate" :hold-type="filterBar.holdType" :disabled="loading.global" - @change="handleFilterChange" + @apply="handleApply" + @hold-type-change="handleHoldTypeChange" /> diff --git a/frontend/src/hold-history/components/FilterBar.vue b/frontend/src/hold-history/components/FilterBar.vue index d0c9cb9..a3103bf 100644 --- a/frontend/src/hold-history/components/FilterBar.vue +++ b/frontend/src/hold-history/components/FilterBar.vue @@ -1,5 +1,5 @@ diff --git a/frontend/src/hold-history/style.css b/frontend/src/hold-history/style.css index fa623b5..96fee02 100644 --- a/frontend/src/hold-history/style.css +++ b/frontend/src/hold-history/style.css @@ -100,6 +100,38 @@ box-shadow: 0 0 0 2px rgba(14, 165, 233, 0.18); } +.filter-action-group { + display: flex; + align-items: flex-end; +} + +.btn-query { + white-space: nowrap; +} + +.btn-query:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +.btn-spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid rgba(255, 255, 255, 0.4); + border-top-color: #fff; + border-radius: 50%; + animation: spin 0.6s linear infinite; + margin-right: 6px; + vertical-align: middle; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + .checkbox-group { display: inline-flex; flex-wrap: wrap; @@ -109,13 +141,24 @@ .checkbox-option { display: inline-flex; align-items: center; - gap: 6px; - padding: 6px 10px; - border-radius: 8px; + gap: 0; + padding: 6px 12px; + border-radius: 999px; border: 1px solid var(--border); - background: #ffffff; + background: #f8fafc; cursor: pointer; font-size: 13px; + color: #475569; + transition: all 0.15s ease; + user-select: none; +} + +.checkbox-option input[type='checkbox'] { + position: absolute; + width: 0; + height: 0; + opacity: 0; + pointer-events: none; } .checkbox-option.active {