// SATMELI — Crear / editar búsqueda, con preview en vivo.
function Field({ label, hint, children, icon }) {
return (
{icon && }
{label}
{hint && {hint} }
{children}
);
}
const inputStyle = {
fontFamily: 'var(--font)', fontSize: 15, color: 'var(--ink)', background: 'var(--surface)',
border: '1.5px solid var(--line-strong)', borderRadius: 'var(--r-md)', padding: '12px 14px',
width: '100%', boxSizing: 'border-box', outline: 'none', letterSpacing: '-0.01em',
transition: 'border-color 160ms var(--ease), box-shadow 160ms var(--ease)',
};
function TextInput(props) {
return ;
}
function PriceInput({ value, onChange, placeholder }) {
return (
$
onChange(Number(e.target.value.replace(/\D/g, '')) || 0)}
className="sat-input" style={{ ...inputStyle, paddingLeft: 28, fontFamily: 'var(--mono)', fontSize: 14 }} />
);
}
function CatPills({ value, onChange }) {
return (
{Object.entries(SAT_CATS).map(([k, c]) => {
const active = value === k;
return (
onChange(k)}
style={{ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '9px 13px', borderRadius: 'var(--r-pill)',
border: `1.5px solid ${active ? c.tint : 'var(--line-strong)'}`, cursor: 'pointer',
background: active ? `color-mix(in srgb, ${c.tint} 13%, var(--surface))` : 'var(--surface)',
color: active ? c.tint : 'var(--muted-2)', fontFamily: 'var(--font)', fontSize: 13, fontWeight: 600,
letterSpacing: '-0.01em', transition: 'all 150ms var(--ease)' }}>
{c.label}
);
})}
);
}
function SelectBox({ value, onChange, options }) {
return (
onChange(e.target.value)}
style={{ ...inputStyle, appearance: 'none', WebkitAppearance: 'none', cursor: 'pointer', paddingRight: 38 }}>
{options.map((o) => {o} )}
);
}
const LOCATIONS = ['Argentina', 'CABA', 'GBA Norte', 'GBA Sur', 'GBA Oeste', 'Córdoba', 'Rosario', 'Mendoza', 'La Plata'];
const FREQS = [
{ value: '5m', label: '5 min' }, { value: '15m', label: '15 min' }, { value: '1h', label: '1 hora' },
{ value: '6h', label: '6 horas' }, { value: '1d', label: '1 día' },
];
function CreateSearch({ initial, mode, onCancel, onSave }) {
const editing = !!initial;
const [f, setF] = React.useState(() => initial ? { matchMode: 'exacta', ...initial } : {
term: '', cat: 'tech', condition: 'cualquiera', priceMin: 0, priceMax: 0,
location: 'Argentina', freq: '1d', notify: true, matchMode: 'exacta',
});
const set = (k, v) => setF((p) => ({ ...p, [k]: v }));
const desktop = mode === 'desktop';
const valid = f.term.trim().length > 1;
const previewSearch = { id: 'sx', ...f, active: true, matches: 0, lastCheck: 0, createdAt: 0, activity: [0,0,0,0,0,0,0,0,0,0,0,0,0,0] };
const form = (
set('term', e.target.value)} autoFocus={!editing} />
set('matchMode', v)}
options={[{ value: 'exacta', label: 'Exacta' }, { value: 'amplia', label: 'Amplia' }]} />
{(f.matchMode || 'exacta') === 'exacta'
? 'Solo productos cuyo nombre contenga todas las palabras del término. Máxima precisión.'
: 'Incluye productos parecidos de la misma marca o categoría. Más cobertura, menos precisión.'}
set('priceMin', v)} placeholder="mínimo" />
—
set('priceMax', v)} placeholder="máximo" />
set('condition', v)}
options={[{ value: 'nuevo', label: 'Nuevo' }, { value: 'usado', label: 'Usado' }, { value: 'cualquiera', label: 'Cualquiera' }]} />
set('cat', v)} />
set('location', v)} options={LOCATIONS} />
set('freq', v)} options={FREQS} />
{/* notificaciones */}
Notificarme al instante
push + Telegram cuando haya match
set('notify', v)} />
);
const preview = (
Vista previa
{}} onToggle={() => {}} />
ASÍ TE AVISA EL BOT
🔔 Nuevo match para {f.term || 'tu búsqueda'}
{condLabel(f.condition)} · {f.location} · {priceRangeLabel(previewSearch)}
);
return (
{editing ? 'Editar centinela' : 'Nuevo centinela'}
{editing ? 'Editar búsqueda' : 'Crear búsqueda'}
{desktop ? (
) : (
<>
{form}
{preview}
>
)}
{/* barra de guardar */}
Cancelar
valid && onSave({ ...previewSearch, id: initial ? initial.id : 's' + Date.now(), matches: initial ? initial.matches : 0,
lastCheck: initial ? initial.lastCheck : 0, createdAt: initial ? initial.createdAt : 0,
activity: initial ? initial.activity : previewSearch.activity })}
style={{ flex: desktop ? '0 0 auto' : 2, opacity: valid ? 1 : 0.5, cursor: valid ? 'pointer' : 'not-allowed', minWidth: desktop ? 220 : undefined }}>
{editing ? 'Guardar cambios' : 'Activar centinela'}
);
}
Object.assign(window, { CreateSearch });