Add search/filter to collections and fuzzy search to files
This commit is contained in:
parent
57aaa96a59
commit
477ebddcd3
|
|
@ -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' },
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue