Add search/filter to collections and fuzzy search to files

This commit is contained in:
Michael Winter 2026-02-19 16:29:41 +01:00
parent 57aaa96a59
commit 477ebddcd3

View file

@ -60,6 +60,13 @@
</button> </button>
</div> </div>
<input
v-model="searchQuery"
type="text"
placeholder="Search..."
class="w-full mb-4 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-black"
/>
<div class="bg-white rounded-lg shadow overflow-hidden mb-8"> <div class="bg-white rounded-lg shadow overflow-hidden mb-8">
<table class="w-full"> <table class="w-full">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
@ -69,7 +76,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="item in items" :key="item.id" class="border-t hover:bg-gray-50"> <tr v-for="item in filteredItems" :key="item.id" class="border-t hover:bg-gray-50">
<td class="px-4 py-3">{{ getTitle(item) }}</td> <td class="px-4 py-3">{{ getTitle(item) }}</td>
<td class="px-4 py-3 space-x-2"> <td class="px-4 py-3 space-x-2">
<button @click="viewRawJson(item)" class="text-green-600 hover:text-green-800">JSON</button> <button @click="viewRawJson(item)" class="text-green-600 hover:text-green-800">JSON</button>
@ -91,6 +98,13 @@
</label> </label>
</div> </div>
<input
v-model="fileSearchQuery"
type="text"
placeholder="Search files..."
class="w-full mb-4 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-black"
/>
<div class="bg-white rounded-lg shadow overflow-hidden"> <div class="bg-white rounded-lg shadow overflow-hidden">
<table class="w-full"> <table class="w-full">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
@ -101,7 +115,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="file in files" :key="file.name" class="border-t hover:bg-gray-50"> <tr v-for="file in filteredFiles" :key="file.name" class="border-t hover:bg-gray-50">
<td class="px-4 py-3">{{ file.name }}</td> <td class="px-4 py-3">{{ file.name }}</td>
<td class="px-4 py-3 text-gray-500">{{ (file.size / 1024).toFixed(1) }} KB</td> <td class="px-4 py-3 text-gray-500">{{ (file.size / 1024).toFixed(1) }} KB</td>
<td class="px-4 py-3 space-x-2"> <td class="px-4 py-3 space-x-2">
@ -134,7 +148,7 @@
</template> </template>
<script setup> <script setup>
import { ref, watch } from 'vue' import { ref, watch, computed } from 'vue'
import { collections } from '@/admin/schemas' import { collections } from '@/admin/schemas'
const password = ref('') const password = ref('')
@ -147,6 +161,40 @@ const files = ref([])
const rawJsonItem = ref(null) const rawJsonItem = ref(null)
const rawJsonContent = ref('') const rawJsonContent = ref('')
const isUploading = ref(false) const isUploading = ref(false)
const searchQuery = ref('')
const fileSearchQuery = ref('')
const searchFields = {
works: ['title', 'type', 'instrument_tags'],
publications: ['entryTags.title', 'entryTags.year', 'citationKey'],
events: ['venue.name', 'venue.city', 'start_date'],
releases: ['title', 'year'],
talks: ['title', 'location', 'date']
}
const filteredItems = computed(() => {
if (!searchQuery.value.trim()) return items.value
const query = searchQuery.value.toLowerCase()
const fields = searchFields[selectedCollection.value] || []
return items.value.filter(item => {
for (const field of fields) {
const value = field.split('.').reduce((obj, key) => obj?.[key], item)
if (value && String(value).toLowerCase().includes(query)) {
return true
}
}
return false
})
})
const filteredFiles = computed(() => {
if (!fileSearchQuery.value.trim()) return files.value
const normalize = (str) => str.toLowerCase().replace(/[\s_-]+/g, '')
const query = normalize(fileSearchQuery.value)
return files.value.filter(file => normalize(file.name).includes(query))
})
const fileFolders = [ const fileFolders = [
{ key: 'scores', label: 'Scores' }, { key: 'scores', label: 'Scores' },