Update
This commit is contained in:
175
client/components/ModalSearch.js
Normal file
175
client/components/ModalSearch.js
Normal file
@@ -0,0 +1,175 @@
|
||||
// components/ModalSearch.js
|
||||
import { $, watch, h, each } from "sigpro";
|
||||
import { db } from "sigpro/utils";
|
||||
import { Modal, Input, Button, Loading, Table } from "sigpro-ui";
|
||||
|
||||
export const ModalSearch = ({ open, onSelect }) => {
|
||||
const search = $('');
|
||||
const results = $([]);
|
||||
const loading = $(false);
|
||||
const error = $(null);
|
||||
const debounceTimer = $(null);
|
||||
|
||||
// Búsqueda con debounce de 500ms
|
||||
watch(search, (query) => {
|
||||
const currentTimer = debounceTimer();
|
||||
if (currentTimer) {
|
||||
clearTimeout(currentTimer);
|
||||
debounceTimer(null);
|
||||
}
|
||||
|
||||
if (!query || query.length < 3) {
|
||||
results([]);
|
||||
error(null);
|
||||
return;
|
||||
}
|
||||
|
||||
loading(true);
|
||||
error(null);
|
||||
|
||||
const timer = setTimeout(async () => {
|
||||
try {
|
||||
const data = await db('/proxy/3000/api/db/search', { q: query });
|
||||
console.log(data)
|
||||
results(data.results || []);
|
||||
} catch (err) {
|
||||
error(err.message);
|
||||
results([]);
|
||||
} finally {
|
||||
loading(false);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
debounceTimer(timer);
|
||||
});
|
||||
|
||||
// Columnas de la tabla
|
||||
const columns = [
|
||||
{ label: 'Código', key: 'CodigoPoliza', class: 'font-mono text-xs' },
|
||||
{ label: 'Mediador', key: 'CodigoMediador', class: 'text-xs' },
|
||||
{ label: 'Nombre', key: 'Nombre', class: 'font-medium text-xs' },
|
||||
{ label: 'Apellidos', key: 'Apellidos', class: 'text-xs' },
|
||||
{ label: 'Documento', key: 'Documento', class: 'text-xs' },
|
||||
{ label: 'Riesgo', key: 'Riesgo', class: 'text-xs' },
|
||||
{ label: 'Ramo', key: 'Ramo', class: 'text-xs' },
|
||||
{ label: 'Localidad', key: 'Localidad', class: 'text-xs' },
|
||||
{
|
||||
label: '',
|
||||
key: 'action',
|
||||
class: 'w-0 p-0',
|
||||
render: (item) => Button({
|
||||
class: 'btn btn-ghost btn-xs',
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
handleSelect(item);
|
||||
}
|
||||
}, 'Seleccionar')
|
||||
}
|
||||
];
|
||||
|
||||
const handleSelect = (item) => {
|
||||
onSelect?.(item);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
open(false);
|
||||
search('');
|
||||
results([]);
|
||||
error(null);
|
||||
};
|
||||
|
||||
return Modal({
|
||||
open,
|
||||
title: 'Buscar Pólizas',
|
||||
class: 'max-w-1024px max-h-612px',
|
||||
actions: [
|
||||
Button({
|
||||
class: 'btn btn-ghost',
|
||||
onclick: closeModal
|
||||
}, 'Cerrar')
|
||||
]
|
||||
}, [
|
||||
h('div', { class: 'space-y-4' }, [
|
||||
// Campo de búsqueda
|
||||
Input({
|
||||
type: "search",
|
||||
class: "w-full",
|
||||
placeholder: "Buscar por cualquier campo (mín. 3 caracteres)...",
|
||||
value: search,
|
||||
left: h('span', { class: "icon-[lucide--search]" }),
|
||||
right: () => loading() ?
|
||||
Loading({ class: "loading-sm" }) :
|
||||
(search() ? Button({
|
||||
class: "btn btn-ghost btn-xs btn-circle -mr-2",
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
search('');
|
||||
}
|
||||
}, h('span', { class: "icon-[lucide--x] opacity-50" })) : null),
|
||||
oninput: (e) => search(e.target.value),
|
||||
autofocus: true
|
||||
}),
|
||||
|
||||
// Estado de carga
|
||||
() => loading() ? h('div', { class: 'flex justify-center items-center py-8' }, [
|
||||
Loading({ class: 'loading-md' }),
|
||||
h('span', { class: 'ml-2 text-sm opacity-70' }, 'Buscando...')
|
||||
]) : null,
|
||||
|
||||
// Error
|
||||
() => !loading() && error() ? h('div', { class: 'alert alert-error py-2 px-3 text-sm' }, [
|
||||
h('span', { class: 'icon-[lucide--alert-circle]' }),
|
||||
h('span', {}, error())
|
||||
]) : null,
|
||||
|
||||
// Contador de resultados
|
||||
() => !loading() && !error() && results().length > 0 ?
|
||||
h('div', { class: 'text-xs opacity-60 px-1' },
|
||||
`${results().length} resultado${results().length !== 1 ? 's' : ''}`
|
||||
)
|
||||
: null,
|
||||
|
||||
// Tabla de resultados
|
||||
() => !loading() && !error() && results().length > 0 ?
|
||||
h('div', { class: 'overflow-x-auto max-h-96' }, [
|
||||
h('table', { class: 'table table-xs table-zebra table-pin-rows w-full' }, [
|
||||
h('thead', {}, [
|
||||
h('tr', {}, columns.map(col =>
|
||||
h('th', { class: col.class || '' }, col.label)
|
||||
))
|
||||
]),
|
||||
h('tbody', {},
|
||||
each(results, (item) =>
|
||||
h('tr', {
|
||||
class: 'hover cursor-pointer',
|
||||
onclick: () => handleSelect(item)
|
||||
}, columns.map(col =>
|
||||
h('td', { class: col.class || '' },
|
||||
col.render ? col.render(item) : (item[col.key] || '')
|
||||
)
|
||||
))
|
||||
)
|
||||
)
|
||||
])
|
||||
])
|
||||
: null,
|
||||
|
||||
// Sin resultados
|
||||
() => !loading() && !error() && search().length >= 3 && results().length === 0 ?
|
||||
h('div', { class: 'text-center py-8 text-base-content/50' }, [
|
||||
h('span', { class: 'icon-[lucide--search-x] text-2xl mb-2 block' }),
|
||||
h('span', { class: 'text-sm' }, 'No se encontraron resultados')
|
||||
])
|
||||
: null,
|
||||
|
||||
// Estado inicial
|
||||
() => !loading() && !error() && search().length < 3 ?
|
||||
h('div', { class: 'text-center py-8 text-base-content/40 text-sm' },
|
||||
'Escribe al menos 3 caracteres para buscar'
|
||||
)
|
||||
: null
|
||||
])
|
||||
]);
|
||||
};
|
||||
89
client/components/SearchResults.js
Normal file
89
client/components/SearchResults.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// components/SearchResults.js
|
||||
import { h, each } from "../sigpro.js";
|
||||
import { Button, Table, Loading } from "sigpro-ui";
|
||||
|
||||
export const SearchResults = ({ results, loading, error, onSelect }) => {
|
||||
const columns = [
|
||||
{ label: 'Código', key: 'CodigoPoliza', class: 'font-mono text-xs' },
|
||||
{ label: 'Mediador', key: 'CodigoMediador', class: 'text-xs' },
|
||||
{ label: 'Nombre', key: 'Nombre', class: 'font-medium text-xs' },
|
||||
{ label: 'Apellidos', key: 'Apellidos', class: 'text-xs' },
|
||||
{ label: 'Documento', key: 'Documento', class: 'text-xs' },
|
||||
{ label: 'Riesgo', key: 'Riesgo', class: 'text-xs' },
|
||||
{ label: 'Ramo', key: 'Ramo', class: 'text-xs' },
|
||||
{ label: 'Localidad', key: 'Localidad', class: 'text-xs' },
|
||||
{
|
||||
label: '',
|
||||
key: 'action',
|
||||
class: 'w-0 p-0',
|
||||
render: (item) => Button({
|
||||
class: 'btn btn-ghost btn-xs',
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
onSelect?.(item);
|
||||
}
|
||||
}, 'Seleccionar')
|
||||
}
|
||||
];
|
||||
|
||||
return h('div', { class: 'p-3 space-y-2' }, [
|
||||
// Estado de carga
|
||||
() => loading() ? h('div', { class: 'flex justify-center items-center py-6' }, [
|
||||
Loading({ class: 'loading-md' }),
|
||||
h('span', { class: 'ml-2 text-sm opacity-70' }, 'Buscando...')
|
||||
]) : null,
|
||||
|
||||
// Error
|
||||
() => !loading() && error() ? h('div', { class: 'alert alert-error py-2 px-3 text-sm' }, [
|
||||
h('span', { class: 'icon-[lucide--alert-circle]' }),
|
||||
h('span', {}, error())
|
||||
]) : null,
|
||||
|
||||
// Contador de resultados
|
||||
() => !loading() && !error() && results().length > 0 ?
|
||||
h('div', { class: 'text-xs opacity-60 px-1' },
|
||||
`${results().length} resultado${results().length !== 1 ? 's' : ''} encontrado${results().length !== 1 ? 's' : ''}`
|
||||
)
|
||||
: null,
|
||||
|
||||
// Tabla de resultados
|
||||
() => !loading() && !error() && results().length > 0 ?
|
||||
h('div', { class: 'overflow-x-auto w-full' }, [
|
||||
h('table', { class: 'table table-xs table-zebra table-pin-rows w-full' }, [
|
||||
h('thead', {}, [
|
||||
h('tr', {}, columns.map(col =>
|
||||
h('th', { class: col.class || '' }, col.label)
|
||||
))
|
||||
]),
|
||||
h('tbody', {},
|
||||
each(results, (item) =>
|
||||
h('tr', {
|
||||
class: 'hover cursor-pointer',
|
||||
onclick: () => onSelect?.(item)
|
||||
}, columns.map(col =>
|
||||
h('td', { class: col.class || '' },
|
||||
col.render ? col.render(item) : (item[col.key] || '')
|
||||
)
|
||||
))
|
||||
)
|
||||
)
|
||||
])
|
||||
])
|
||||
: null,
|
||||
|
||||
// Sin resultados
|
||||
() => !loading() && !error() && results().length === 0 ?
|
||||
h('div', { class: 'text-center py-6 text-base-content/50' }, [
|
||||
h('span', { class: 'icon-[lucide--search-x] text-2xl mb-2 block' }),
|
||||
h('span', { class: 'text-sm' }, 'No se encontraron resultados')
|
||||
])
|
||||
: null,
|
||||
|
||||
// Estado inicial (menos de 3 caracteres)
|
||||
() => !loading() && !error() && results().length === 0 ?
|
||||
h('div', { class: 'text-center py-4 text-base-content/40 text-xs' },
|
||||
'Escribe al menos 3 caracteres para buscar'
|
||||
)
|
||||
: null
|
||||
]);
|
||||
};
|
||||
Reference in New Issue
Block a user