Compare commits
3 Commits
0ea3bd0859
...
73cd1ead70
| Author | SHA1 | Date |
|---|---|---|
|
|
73cd1ead70 | |
|
|
50215fc657 | |
|
|
87f4f04d2c |
|
|
@ -592,6 +592,21 @@ function clearLocalQuery() {
|
||||||
|
|
||||||
const localQuery = ref('')
|
const localQuery = ref('')
|
||||||
const debouncedLocalQuery = useDebounce(localQuery, 200)
|
const debouncedLocalQuery = useDebounce(localQuery, 200)
|
||||||
|
const showInternalSearch = ref(true)
|
||||||
|
const internalSearchRef = ref<{ input?: HTMLInputElement } | null>(null)
|
||||||
|
const metaExpanded = ref(true)
|
||||||
|
|
||||||
|
function toggleInternalSearch() {
|
||||||
|
showInternalSearch.value = !showInternalSearch.value
|
||||||
|
if (showInternalSearch.value) {
|
||||||
|
metaExpanded.value = true
|
||||||
|
nextTick(() => internalSearchRef.value?.input?.focus())
|
||||||
|
} else {
|
||||||
|
clearLocalQuery()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.document?.id, () => { metaExpanded.value = true; showInternalSearch.value = true })
|
||||||
|
|
||||||
watch(debouncedLocalQuery, (q) => {
|
watch(debouncedLocalQuery, (q) => {
|
||||||
// Cualquier cambio en el input activa el Estado 2
|
// Cualquier cambio en el input activa el Estado 2
|
||||||
|
|
@ -791,17 +806,7 @@ function onSelectionChange() {
|
||||||
if (!sel || sel.toString().trim().length === 0) clearSelectionTooltip()
|
if (!sel || sel.toString().trim().length === 0) clearSelectionTooltip()
|
||||||
}
|
}
|
||||||
|
|
||||||
const links = computed(() => {
|
const links = computed(() => [])
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: 'Ver en sitio',
|
|
||||||
icon: 'ph-arrow-square-out',
|
|
||||||
to: matchUrl,
|
|
||||||
target: '_blank'
|
|
||||||
},
|
|
||||||
...formatFiles(props.document?.files)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const items = computed(() => {
|
const items = computed(() => {
|
||||||
return [
|
return [
|
||||||
|
|
@ -830,6 +835,15 @@ const items = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #right>
|
<template #right>
|
||||||
|
<UTooltip :text="showInternalSearch ? 'Cerrar buscador' : 'Buscar en documento'">
|
||||||
|
<UButton
|
||||||
|
icon="i-lucide-search"
|
||||||
|
:color="showInternalSearch || !!localQuery ? 'primary' : 'neutral'"
|
||||||
|
:variant="showInternalSearch || !!localQuery ? 'soft' : 'ghost'"
|
||||||
|
:disabled="!paragraphs.length"
|
||||||
|
@click="toggleInternalSearch"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
<UTooltip text="Como funciona?">
|
<UTooltip text="Como funciona?">
|
||||||
<UButton
|
<UButton
|
||||||
icon="ph-student"
|
icon="ph-student"
|
||||||
|
|
@ -853,8 +867,17 @@ const items = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
|
<!-- Strip siempre visible para expandir/colapsar metadatos -->
|
||||||
|
<button
|
||||||
|
class="w-full flex items-center justify-center gap-1.5 py-1 border-b border-default bg-elevated/60 hover:bg-elevated transition-colors text-xs text-muted"
|
||||||
|
@click="metaExpanded = !metaExpanded"
|
||||||
|
>
|
||||||
|
<UIcon :name="metaExpanded ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'" class="size-3.5" />
|
||||||
|
<span>{{ metaExpanded ? 'Ocultar detalles' : 'Ver detalles' }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Metadata -->
|
<!-- Metadata -->
|
||||||
<div class="flex flex-col gap-3 p-4 sm:px-6 border-b border-default shadow-sm z-10">
|
<div v-show="metaExpanded" class="flex flex-col gap-3 p-4 sm:px-6 border-b border-default shadow-sm z-10">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-2">
|
<div class="flex flex-wrap items-start justify-between gap-2">
|
||||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 min-w-0">
|
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 min-w-0">
|
||||||
<p v-if="document?.draft" class="text-sm text-highlighted flex items-center gap-1.5 shrink-0">
|
<p v-if="document?.draft" class="text-sm text-highlighted flex items-center gap-1.5 shrink-0">
|
||||||
|
|
@ -887,24 +910,12 @@ const items = computed(() => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="fileLinks.length" class="flex flex-wrap gap-2">
|
<!-- file links hidden -->
|
||||||
<UButton
|
|
||||||
v-for="(f, idx) in fileLinks"
|
|
||||||
:key="idx"
|
|
||||||
:to="f.to"
|
|
||||||
:target="f.target"
|
|
||||||
:icon="f.icon!"
|
|
||||||
:label="f.label"
|
|
||||||
color="neutral"
|
|
||||||
variant="subtle"
|
|
||||||
size="xs"
|
|
||||||
external
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Buscador interno del documento -->
|
<!-- Buscador interno del documento -->
|
||||||
<div v-if="paragraphs.length" class="flex items-center gap-2">
|
<div v-if="paragraphs.length" v-show="showInternalSearch || !!localQuery" class="flex items-center gap-2">
|
||||||
<UInput
|
<UInput
|
||||||
|
ref="internalSearchRef"
|
||||||
v-model="localQuery"
|
v-model="localQuery"
|
||||||
icon="i-lucide-search"
|
icon="i-lucide-search"
|
||||||
:placeholder="props.query || 'Buscar en este documento...'"
|
:placeholder="props.query || 'Buscar en este documento...'"
|
||||||
|
|
@ -1021,17 +1032,6 @@ const items = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
</UPageBody>
|
</UPageBody>
|
||||||
|
|
||||||
<template #right>
|
|
||||||
<UPageAside>
|
|
||||||
<UPageAnchors :links="links" v-if="paragraphs.length" />
|
|
||||||
|
|
||||||
<UAccordion :items="items">
|
|
||||||
<template #body="{ item }">
|
|
||||||
<pre>{{ item.content }}</pre>
|
|
||||||
</template>
|
|
||||||
</UAccordion>
|
|
||||||
</UPageAside>
|
|
||||||
</template>
|
|
||||||
</UPage>
|
</UPage>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ interface EntrelineaDoc {
|
||||||
page?: number | string
|
page?: number | string
|
||||||
text?: string
|
text?: string
|
||||||
html?: string
|
html?: string
|
||||||
|
draft?: boolean
|
||||||
studies?: Study[]
|
studies?: Study[]
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +40,8 @@ const showStudies = ref(false)
|
||||||
// Volver al detalle cuando cambia el documento
|
// Volver al detalle cuando cambia el documento
|
||||||
watch(() => props.document?.id, () => { showStudies.value = false })
|
watch(() => props.document?.id, () => { showStudies.value = false })
|
||||||
|
|
||||||
|
const { unlocked } = useDevMode()
|
||||||
|
|
||||||
const imageUrl = computed<string | null>(() =>
|
const imageUrl = computed<string | null>(() =>
|
||||||
getEntrelineaImageUrl(props.document?.image, 'detail')
|
getEntrelineaImageUrl(props.document?.image, 'detail')
|
||||||
)
|
)
|
||||||
|
|
@ -268,8 +271,6 @@ function onLightboxTouchStart(e: TouchEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLightboxTouchMove(e: TouchEvent) {
|
function onLightboxTouchMove(e: TouchEvent) {
|
||||||
// Prevent page scroll while interacting inside the lightbox
|
|
||||||
e.preventDefault()
|
|
||||||
if (e.touches.length === 1 && lightboxScale.value > 1) {
|
if (e.touches.length === 1 && lightboxScale.value > 1) {
|
||||||
const dx = e.touches[0].clientX - lbLastTouchX
|
const dx = e.touches[0].clientX - lbLastTouchX
|
||||||
const dy = e.touches[0].clientY - lbLastTouchY
|
const dy = e.touches[0].clientY - lbLastTouchY
|
||||||
|
|
@ -391,13 +392,13 @@ const lightboxImgStyle = computed(() => ({
|
||||||
<div class="p-4 sm:p-6 pb-10 flex flex-col gap-6">
|
<div class="p-4 sm:p-6 pb-10 flex flex-col gap-6">
|
||||||
|
|
||||||
<!-- Imagen: mobile toca para abrir lightbox, desktop zoom al hover -->
|
<!-- Imagen: mobile toca para abrir lightbox, desktop zoom al hover -->
|
||||||
<template v-if="imageUrl">
|
<template v-if="imageUrl && (!document.draft || unlocked)">
|
||||||
<!-- Mobile: imagen con indicador de zoom táctil -->
|
<!-- Mobile: imagen con indicador de zoom táctil -->
|
||||||
<div
|
<div
|
||||||
class="lg:hidden rounded-xl overflow-hidden border border-default shadow-sm bg-elevated relative cursor-pointer"
|
class="lg:hidden h-52 rounded-xl overflow-hidden border border-default shadow-sm bg-elevated relative cursor-pointer flex items-center justify-center"
|
||||||
@click="openLightbox"
|
@click="openLightbox"
|
||||||
>
|
>
|
||||||
<img :src="imageUrl" :alt="title" loading="lazy" class="w-full h-auto select-none">
|
<img :src="imageUrl" :alt="title" loading="lazy" class="w-full h-full object-contain select-none">
|
||||||
<div class="absolute bottom-2 right-2 bg-black/50 rounded-full p-1.5 pointer-events-none">
|
<div class="absolute bottom-2 right-2 bg-black/50 rounded-full p-1.5 pointer-events-none">
|
||||||
<UIcon name="i-lucide-zoom-in" class="size-4 text-white" />
|
<UIcon name="i-lucide-zoom-in" class="size-4 text-white" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -406,7 +407,7 @@ const lightboxImgStyle = computed(() => ({
|
||||||
<!-- Desktop: hover zoom con lente -->
|
<!-- Desktop: hover zoom con lente -->
|
||||||
<div
|
<div
|
||||||
ref="zoomBoxRef"
|
ref="zoomBoxRef"
|
||||||
class="hidden lg:flex rounded-xl bg-elevated border border-default shadow-sm p-4 items-center justify-center relative cursor-zoom-in select-none min-h-48"
|
class="hidden lg:flex h-52 rounded-xl bg-elevated border border-default shadow-sm p-4 items-center justify-center relative cursor-zoom-in select-none"
|
||||||
@mouseenter="onZoomEnter"
|
@mouseenter="onZoomEnter"
|
||||||
@mouseleave="onZoomLeave"
|
@mouseleave="onZoomLeave"
|
||||||
@mousemove="onZoomMove"
|
@mousemove="onZoomMove"
|
||||||
|
|
@ -430,13 +431,13 @@ const lightboxImgStyle = computed(() => ({
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Sin imagen -->
|
<!-- Sin imagen / Borrador -->
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="rounded-xl border border-dashed border-default p-8 text-center text-xs text-dimmed"
|
class="rounded-xl border border-dashed border-default p-8 text-center text-xs text-dimmed"
|
||||||
>
|
>
|
||||||
<UIcon name="i-lucide-image-off" class="size-6 mb-1 mx-auto" />
|
<UIcon name="i-lucide-image-off" class="size-6 mb-1 mx-auto" />
|
||||||
<p>Sin imagen disponible</p>
|
<p>Imagen no disponible</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Texto -->
|
<!-- Texto -->
|
||||||
|
|
@ -519,31 +520,47 @@ const lightboxImgStyle = computed(() => ({
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
<!-- Lightbox mobile con pinch-to-zoom -->
|
<!-- Popup móvil: imagen con pinch-to-zoom -->
|
||||||
<Teleport to="body">
|
<UModal
|
||||||
<div
|
v-model:open="lightboxOpen"
|
||||||
v-if="lightboxOpen"
|
:ui="{
|
||||||
class="lg:hidden fixed inset-0 bg-black z-[9999] flex items-center justify-center"
|
overlay: 'bg-black/90',
|
||||||
@touchstart.passive="onLightboxTouchStart"
|
content: 'bg-black text-white max-w-[95vw] sm:max-w-lg',
|
||||||
@touchmove.prevent="onLightboxTouchMove"
|
header: 'py-2 px-3 border-b-0',
|
||||||
@touchend="onLightboxTouchEnd"
|
body: 'p-0',
|
||||||
@click.self="closeLightbox"
|
}"
|
||||||
>
|
>
|
||||||
<button
|
<template #header>
|
||||||
class="absolute top-4 right-4 z-10 text-white bg-black/60 rounded-full p-2"
|
<div class="w-full flex justify-end">
|
||||||
@click="closeLightbox"
|
<UButton
|
||||||
|
icon="i-lucide-x"
|
||||||
|
size="sm"
|
||||||
|
color="neutral"
|
||||||
|
variant="ghost"
|
||||||
|
class="text-white hover:bg-white/20"
|
||||||
|
@click="closeLightbox"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div
|
||||||
|
class="touch-none flex items-center justify-center overflow-hidden"
|
||||||
|
style="height: 70dvh"
|
||||||
|
@touchstart.passive="onLightboxTouchStart"
|
||||||
|
@touchmove.passive="onLightboxTouchMove"
|
||||||
|
@touchend.passive="onLightboxTouchEnd"
|
||||||
>
|
>
|
||||||
<UIcon name="i-lucide-x" class="size-6" />
|
<img
|
||||||
</button>
|
v-if="imageUrl"
|
||||||
<img
|
:src="imageUrl"
|
||||||
:src="imageUrl!"
|
:alt="title"
|
||||||
:alt="title"
|
class="w-full h-full object-contain select-none"
|
||||||
class="max-w-full max-h-full object-contain select-none"
|
:style="lightboxImgStyle"
|
||||||
:style="lightboxImgStyle"
|
draggable="false"
|
||||||
draggable="false"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</Teleport>
|
</UModal>
|
||||||
|
|
||||||
</UDashboardPanel>
|
</UDashboardPanel>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -434,21 +434,7 @@ function onInputKey(e: KeyboardEvent) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fila 2: archivos adjuntos -->
|
<!-- file links hidden -->
|
||||||
<div v-if="fileLinks.length" class="flex flex-wrap gap-2">
|
|
||||||
<UButton
|
|
||||||
v-for="(f, idx) in fileLinks"
|
|
||||||
:key="idx"
|
|
||||||
:to="f.to"
|
|
||||||
:target="f.target"
|
|
||||||
:icon="f.icon!"
|
|
||||||
:label="f.label"
|
|
||||||
color="neutral"
|
|
||||||
variant="subtle"
|
|
||||||
size="xs"
|
|
||||||
external
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Fila 3: búsqueda local en el documento -->
|
<!-- Fila 3: búsqueda local en el documento -->
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,8 @@ const colors = computed(() => {
|
||||||
|
|
||||||
// ---- State ----------------------------------------------------------------
|
// ---- State ----------------------------------------------------------------
|
||||||
|
|
||||||
|
const exactSearch = ref(false)
|
||||||
|
|
||||||
const groupedHits = ref<SearchGroup[]>([])
|
const groupedHits = ref<SearchGroup[]>([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
|
|
@ -257,7 +259,7 @@ async function runSearch(q: string, page = 1, append = false) {
|
||||||
multiSearchSearchesParameter: {
|
multiSearchSearchesParameter: {
|
||||||
searches: [{
|
searches: [{
|
||||||
collection: props.paragraphsCollection,
|
collection: props.paragraphsCollection,
|
||||||
q: q || '*',
|
q: exactSearch.value && q ? `"${q}"` : q || '*',
|
||||||
queryBy: QUERY_BY,
|
queryBy: QUERY_BY,
|
||||||
filterBy: filterBy.value,
|
filterBy: filterBy.value,
|
||||||
perPage: settings.pageSize,
|
perPage: settings.pageSize,
|
||||||
|
|
@ -422,6 +424,10 @@ watch(debouncedQuery, (q) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(exactSearch, () => {
|
||||||
|
if (query.value.trim()) runSearch(query.value, 1, false)
|
||||||
|
})
|
||||||
|
|
||||||
// ---- Selección y carga del detalle ----------------------------------------
|
// ---- Selección y carga del detalle ----------------------------------------
|
||||||
|
|
||||||
const selectedDocId = ref<string | null>(null)
|
const selectedDocId = ref<string | null>(null)
|
||||||
|
|
@ -576,15 +582,30 @@ function metaLocation(meta: DocMeta | undefined): string {
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<div class="px-4 sm:px-6 py-3 border-b border-default" id="inputField">
|
<div class="px-4 sm:px-6 py-3 border-b border-default flex items-center gap-2" id="inputField">
|
||||||
<UInput
|
<UInput
|
||||||
v-model="query"
|
v-model="query"
|
||||||
icon="i-lucide-search"
|
icon="i-lucide-search"
|
||||||
:placeholder="t('search.placeholder')"
|
:placeholder="t('search.placeholder')"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
size="md"
|
size="md"
|
||||||
class="w-full"
|
class="flex-1 min-w-0"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
class="flex rounded-full p-0.5 shrink-0 transition-colors duration-200"
|
||||||
|
:class="exactSearch ? 'bg-primary' : 'bg-gray-200 dark:bg-gray-700'"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="px-2.5 py-0.5 rounded-full text-xs transition-all duration-200 whitespace-nowrap"
|
||||||
|
:class="!exactSearch ? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white font-semibold shadow-sm' : 'text-white/40 font-normal'"
|
||||||
|
@click.stop="exactSearch = false"
|
||||||
|
>{{ t('search.word') }}</button>
|
||||||
|
<button
|
||||||
|
class="px-2.5 py-0.5 rounded-full text-xs transition-all duration-200 whitespace-nowrap"
|
||||||
|
:class="exactSearch ? 'bg-white text-primary font-semibold shadow-sm' : 'text-gray-400 dark:text-gray-400 font-normal'"
|
||||||
|
@click.stop="exactSearch = true"
|
||||||
|
>{{ t('search.phrase') }}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UAlert
|
<UAlert
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ const debouncedQuery = useDebounce(query, 150)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const loadingMore = ref(false)
|
const loadingMore = ref(false)
|
||||||
const errorMsg = ref<string | null>(null)
|
const errorMsg = ref<string | null>(null)
|
||||||
|
const exactSearch = ref(false)
|
||||||
|
|
||||||
interface Study {
|
interface Study {
|
||||||
id?: number
|
id?: number
|
||||||
|
|
@ -53,6 +54,7 @@ interface EntrelineaDoc {
|
||||||
page?: number | string
|
page?: number | string
|
||||||
text?: string
|
text?: string
|
||||||
html?: string
|
html?: string
|
||||||
|
draft?: boolean
|
||||||
studies?: Study[]
|
studies?: Study[]
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +122,7 @@ async function runSearch(q: string, page = 1, append = false) {
|
||||||
multiSearchSearchesParameter: {
|
multiSearchSearchesParameter: {
|
||||||
searches: [{
|
searches: [{
|
||||||
collection: COLLECTION,
|
collection: COLLECTION,
|
||||||
q: q || '*',
|
q: exactSearch.value && q ? `"${q}"` : q || '*',
|
||||||
queryBy: QUERY_BY,
|
queryBy: QUERY_BY,
|
||||||
includeFields: INCLUDE_FIELDS,
|
includeFields: INCLUDE_FIELDS,
|
||||||
filterBy: filterBy.value,
|
filterBy: filterBy.value,
|
||||||
|
|
@ -188,6 +190,10 @@ watch(debouncedQuery, (q) => {
|
||||||
runSearch(q, 1, false)
|
runSearch(q, 1, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(exactSearch, () => {
|
||||||
|
if (query.value.trim()) runSearch(query.value, 1, false)
|
||||||
|
})
|
||||||
|
|
||||||
const selected = ref<EntrelineaDoc | null>(null)
|
const selected = ref<EntrelineaDoc | null>(null)
|
||||||
|
|
||||||
const isPanelOpen = computed({
|
const isPanelOpen = computed({
|
||||||
|
|
@ -311,14 +317,31 @@ function highlightedFor(hit: TypesenseHit, field: string): string | null {
|
||||||
<!-- Funcional: se muestra cuando la clave de desarrollador es correcta -->
|
<!-- Funcional: se muestra cuando la clave de desarrollador es correcta -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="px-4 sm:px-6 py-3 border-b border-default">
|
<div class="px-4 sm:px-6 py-3 border-b border-default">
|
||||||
<UInput
|
<div class="flex items-center gap-2">
|
||||||
v-model="query"
|
<UInput
|
||||||
icon="i-lucide-search"
|
v-model="query"
|
||||||
:placeholder="t('Search.placeholder')"
|
icon="i-lucide-search"
|
||||||
:loading="loading"
|
:placeholder="t('Search.placeholder')"
|
||||||
size="md"
|
:loading="loading"
|
||||||
class="w-full"
|
size="md"
|
||||||
/>
|
class="flex-1 min-w-0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex rounded-full p-0.5 shrink-0 transition-colors duration-200"
|
||||||
|
:class="exactSearch ? 'bg-primary' : 'bg-gray-200 dark:bg-gray-700'"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="px-2.5 py-0.5 rounded-full text-xs transition-all duration-200 whitespace-nowrap"
|
||||||
|
:class="!exactSearch ? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white font-semibold shadow-sm' : 'text-white/40 font-normal'"
|
||||||
|
@click.stop="exactSearch = false"
|
||||||
|
>{{ t('search.word') }}</button>
|
||||||
|
<button
|
||||||
|
class="px-2.5 py-0.5 rounded-full text-xs transition-all duration-200 whitespace-nowrap"
|
||||||
|
:class="exactSearch ? 'bg-white text-primary font-semibold shadow-sm' : 'text-gray-400 dark:text-gray-400 font-normal'"
|
||||||
|
@click.stop="exactSearch = true"
|
||||||
|
>{{ t('search.phrase') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<p class="mt-1.5 flex items-center gap-1 text-[11px] text-dimmed">
|
<p class="mt-1.5 flex items-center gap-1 text-[11px] text-dimmed">
|
||||||
<UIcon name="i-lucide-database" class="size-3" />
|
<UIcon name="i-lucide-database" class="size-3" />
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -387,6 +410,14 @@ function highlightedFor(hit: TypesenseHit, field: string): string | null {
|
||||||
color="neutral"
|
color="neutral"
|
||||||
class="mb-1 uppercase"
|
class="mb-1 uppercase"
|
||||||
/>
|
/>
|
||||||
|
<UBadge
|
||||||
|
v-if="hit.document?.draft"
|
||||||
|
label="Borrador"
|
||||||
|
size="sm"
|
||||||
|
variant="subtle"
|
||||||
|
color="warning"
|
||||||
|
class="mb-1"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<UTooltip :text="isFav(hit.document) ? 'Quitar de mi lista' : 'Guardar en mi lista'">
|
<UTooltip :text="isFav(hit.document) ? 'Quitar de mi lista' : 'Guardar en mi lista'">
|
||||||
<UButton
|
<UButton
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ const links = ref<ButtonProps[]>([
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<UDashboardPanel id="changelog-panel" grow>
|
<UDashboardPanel id="changelog-panel" grow>
|
||||||
<UDashboardNavbar :title="t('nav.home')">
|
<UDashboardNavbar :title="t('nav.home')">
|
||||||
<template #leading>
|
<template #leading>
|
||||||
|
|
@ -41,33 +40,62 @@ const links = ref<ButtonProps[]>([
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<UPage>
|
<div class="flex-1 overflow-y-auto">
|
||||||
<UPageHero title="Buscador Carpa"
|
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
|
||||||
:description="$t('home.instructions')"
|
<div class="flex flex-col lg:flex-row lg:items-start gap-8 lg:gap-12">
|
||||||
:headline="`${release?.date}`" orientation="horizontal"
|
|
||||||
:links="links"
|
|
||||||
>
|
|
||||||
|
|
||||||
<div id="index_changelog">
|
<!-- Hero: título, descripción y accesos -->
|
||||||
<UPageCard variant="soft" icon="ph-git-branch" :title="release?.title" :description="release?.description">
|
<div class="flex-1 flex flex-col gap-6">
|
||||||
|
<UBadge variant="subtle" color="neutral" size="sm" class="w-fit">
|
||||||
|
{{ release?.date }}
|
||||||
|
</UBadge>
|
||||||
|
|
||||||
<template #header>
|
<div>
|
||||||
v{{ release?.version }}
|
<h1 class="text-3xl sm:text-4xl font-bold text-highlighted tracking-tight">
|
||||||
</template>
|
Buscador Carpa
|
||||||
|
</h1>
|
||||||
|
<p class="mt-3 text-base text-muted leading-relaxed">
|
||||||
|
{{ $t('home.instructions') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row flex-wrap gap-3">
|
||||||
|
<UButton
|
||||||
|
v-for="link in links"
|
||||||
|
:key="link.label"
|
||||||
|
v-bind="link"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Changelog card -->
|
||||||
|
<div id="index_changelog" class="w-full lg:w-80 xl:w-96 shrink-0">
|
||||||
|
<UCard variant="soft">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<UIcon name="ph-git-branch" class="size-4 text-muted shrink-0" />
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<p class="font-semibold text-highlighted text-sm truncate">{{ release?.title }}</p>
|
||||||
|
<p v-if="release?.description" class="text-xs text-muted truncate">{{ release?.description }}</p>
|
||||||
|
</div>
|
||||||
|
<UBadge variant="outline" color="neutral" size="xs" class="shrink-0">
|
||||||
|
v{{ release?.version }}
|
||||||
|
</UBadge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li v-for="(change, i) in release?.changes" :key="i" class="flex items-start gap-2 text-sm">
|
||||||
|
<UBadge :color="typeConfig[change.type].color as any" variant="subtle" size="xs" class="mt-0.5 shrink-0">
|
||||||
|
{{ typeConfig[change.type].label }}
|
||||||
|
</UBadge>
|
||||||
|
<span class="text-default leading-snug">{{ change.text }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</UCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Lista de cambios -->
|
|
||||||
<ul class="space-y-2">
|
|
||||||
<li v-for="(change, i) in release?.changes" :key="i" class="flex items-start gap-2 text-sm">
|
|
||||||
<UBadge :color="typeConfig[change.type].color as any" variant="subtle" size="xs" class="mt-0.5 shrink-0">
|
|
||||||
{{ typeConfig[change.type].label }}
|
|
||||||
</UBadge>
|
|
||||||
<span class="text-default">{{ change.text }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</UPageCard>
|
|
||||||
</div>
|
</div>
|
||||||
</UPageHero>
|
</div>
|
||||||
|
</div>
|
||||||
</UPage>
|
|
||||||
</UDashboardPanel>
|
</UDashboardPanel>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
"changelog": "What's New"
|
"changelog": "What's New"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"word": "Word",
|
||||||
|
"phrase": "Phrase",
|
||||||
"placeholder": "Search for...",
|
"placeholder": "Search for...",
|
||||||
"searching": "Searching...",
|
"searching": "Searching...",
|
||||||
"tip": "Tip: wrap in \"quotes\" for exact phrase in that order.",
|
"tip": "Tip: wrap in \"quotes\" for exact phrase in that order.",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@
|
||||||
"hits_per_page": "aciertos por página",
|
"hits_per_page": "aciertos por página",
|
||||||
"hits_retrieved_in": "aciertos logrados en",
|
"hits_retrieved_in": "aciertos logrados en",
|
||||||
"for": "Buscando",
|
"for": "Buscando",
|
||||||
|
"word": "Palabra",
|
||||||
|
"phrase": "Frase",
|
||||||
"words": "palabras",
|
"words": "palabras",
|
||||||
"phrases": "frases",
|
"phrases": "frases",
|
||||||
"words_tooltip": "Buscar por palabras",
|
"words_tooltip": "Buscar por palabras",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
"changelog": "Nouveautés"
|
"changelog": "Nouveautés"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"word": "Mot",
|
||||||
|
"phrase": "Phrase",
|
||||||
"placeholder": "Rechercher des activités",
|
"placeholder": "Rechercher des activités",
|
||||||
"publication": "Publicación",
|
"publication": "Publicación",
|
||||||
"draft": ""
|
"draft": ""
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
"changelog": "Novidades"
|
"changelog": "Novidades"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"word": "Palavra",
|
||||||
|
"phrase": "Frase",
|
||||||
"placeholder": "Digite para pesquisar...",
|
"placeholder": "Digite para pesquisar...",
|
||||||
"publication": "Publicação",
|
"publication": "Publicação",
|
||||||
"draft": "Borrador",
|
"draft": "Borrador",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue