Commit Graph
107 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 4.6 eb436638fb fix: AI tab settings not saving — read from DOM in bot-config save JS
The JS FormData was built from the CONFIG object (DB state), missing
all AI inputs the user types: api_key, models, checkboxes (nlu,
ai_for_media). Now reads them directly from DOM before the fetch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 22:04:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9e36a965c9 feat: NLU routing + audio/image transcription via AI
- NluRouter in AiBot: builds permission-filtered flow catalog, asks AI
  to route user intent to exact flow key or return chat response
- MediaTranscriber: downloads WA media, transcribes audio (Whisper/Gemini),
  extracts image intent (OpenAI/Gemini/Claude vision)
- BotRouter hybrid: text → NormalBot → NLU fallback if no match;
  audio/image → transcribe → NLU route; sends transcript preview first
- Admin IA tab: NLU checkbox with description
- Per-company nlu flag saved in config_json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 21:50:01 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2b859e3dc0 feat: multi-provider AI with per-company override and Claude support
- Global settings: custom AI panel with OpenAI/Gemini/Claude each with
  API key, model selector, and live test-connection button (no save needed)
- Per-company IA tab: same three providers + fallback to global when empty
- AiBot: reads per-company config_json first, falls back to env() global
- Added callClaude() for Anthropic Messages API
- More OpenAI models: gpt-4.1, gpt-4.1-mini
- test-ai endpoint: accepts provider/api_key/model from POST for live test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 19:06:51 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b82fcf6aaa feat: add Gemini 2.5 Flash as default model option
Added gemini-2.5-flash to the admin panel selector (first/default option)
and updated the fallback default in AiBot and migrate seed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 18:52:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5883801989 fix: prepend api_base_url to relative endpoint URLs in NormalBot
After migration stripped base URLs from DB records, bot was calling
relative paths (e.g. /ruta) as-is without the base. buildUrl() helper
now prepends api_base_url for all four curl call sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 16:25:10 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f06be3584f feat: two-message post-selection flow in multi-company pre-menu
After user selects a company, send short confirmation text then reset
bot_context and trigger BotRouter with empty message to fire the greeting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 16:18:41 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8f43ccf4c2 feat: pre-menú de selección de empresa para números multi-empresa
Cuando un número pertenece a más de una empresa, el bot muestra un
menú interactivo "¿Con cuál empresa deseas comunicarte?" antes de
cualquier flujo normal. La selección se cachea 8h en la tabla
multi_company_sessions. Keywords de reinicio (salir, inicio, menú...)
limpian la sesión y vuelven a mostrar el pre-menú.
Números de una sola empresa: flujo intacto.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 16:07:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b349230362 chore: script de migración para quitar base URL de endpoints
Recorre company_endpoints y elimina el prefijo api_base_url de
las URLs absolutas, dejando solo la ruta relativa.
Dry-run por defecto (?run=1 para ejecutar).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 15:35:58 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 43933bfe3a feat: endpoints usan api_base_url como base, rutas relativas en UI
- Banner en tab Endpoints muestra la URL base de General
- Inputs de URL aceptan rutas relativas (?param=x o /ruta)
- Preview live de la URL completa (base+ruta) bajo cada input
- Backend prepend api_base_url en test y sync si URL no es absoluta
- Compatible hacia atrás: URLs absolutas existentes siguen funcionando

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 15:19:46 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 bfbc70451d feat: clonar endpoints desde otra empresa en pestaña Endpoints API
Agrega panel de clonación en la pestaña Endpoints API del editor de empresa.
Permite copiar los endpoints de otra empresa con dos modos: sobreescribir todo
o solo añadir los endpoints que aún no tienen URL configurada.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:50:39 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 34322ae3dc Change "Reporte enviado" to "Reporte generado"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:10:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 97a48d0fe4 Validate dynamic_list input and fix cosecha button IDs
- dynamic_list now stores valid IDs in collecting state; rejects
  free-text input that doesn't match a list option
- Fixed submenu_ciclo_cosecha button IDs to match real flow names
  (ciclo_cosecha_dia / ciclo_cosecha_historico)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:07:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0ce766a9eb feat: kilos por lotes con selección de período (mes actual/anterior/personalizado)
- Add last_month date_mode to executeApiReport
- prod_kilos_lotes now shows period menu instead of immediately asking dates
- lotes_mes_actual / lotes_mes_anterior use api_report with date_mode
- lotes_personalizado collects fecha_desde + fecha_hasta (2 messages only when needed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:35:12 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 601169525e fix: dedicated save button for categories + preserve all extra DB keys
- Replace onchange+AJAX with explicit button '💾 Guardar categorías'
  that shows alert on success/error for clear feedback
- Get company_id from DOM (not COMPANY_ID JS const from another script block)
- Main form save now preserves welcome_menu and per_type from DB even if
  POST is truncated by max_input_vars
- Restore welcome_menu='welcome_msg' in DB (was wiped by a previous save)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:16:36 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ae5b7266d4 fix: per_type is DB-only, main form save never touches it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:11:54 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3c79fac8d7 fix: preserve per_type from DB on main form save
The main save rebuilt config from scratch — per_type always started empty,
so clicking Guardar wiped whatever savePerType() had just stored.

Now: load existing DB config first, use its per_type as the base, apply
POST per_type_menu on top only when it is present. Same for welcome_menu.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:09:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8f51659f1e fix: save per-category menus via dedicated endpoint, not the main form
The main form exceeded max_input_vars (27 flows × 37 fields each) so PHP
silently dropped per_type_menu before reaching the save handler.

- Add POST /admin/bot-config/save-per-type — reads existing DB config,
  merges only the per_type_menu values, saves back
- Per-type selects fire savePerType() onchange → AJAX → toast on success
- Add public/.user.ini with max_input_vars=5000 as belt+suspenders
- Remove array_is_list() (PHP 8.1) — replaced with PHP 7.4 compatible check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:06:13 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cae448a9c6 fix: use $menus after it is defined (was undefined in welcomeMenuOpts loop)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:02:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9b0e6ecb71 fix: PHP 7.4 compat — remove union return types from session handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 18:53:32 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 37eef6a705 fix: persist admin sessions in DB so deploys don't log out users
Store PHP sessions in the `sessions` MySQL table (30-day TTL) instead of
server files, which disappear on container restart / redeploy.
Cookie lifetime also extended to 30 days.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 18:42:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b288b07ff5 feat: welcome message, fix per_type save, add missing ciclos flows
- Show welcome_msg (button with "Ver menú") for all new sessions before category menu
- Fix saveConfig() AJAX to include per_type_menu DOM values so they survive individual saves
- Add welcome_menu field to General tab in admin (render + save)
- Add 'atras' command alias (→ show_main_menu)
- Add ciclos_plagas, ciclos_palm, ciclos_tratamientos flows to DB config
- Fix per_type in DB from [] to {} (proper JSON object)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 18:39:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e9bbf8efce feat: per-type greeting_menu takes priority over global show_main_menu
When user says "menu/inicio/volver", bot now shows the menu configured
for their category (per_type.greeting_menu) instead of the global
show_main_menu. Same redirect applies to interactive button taps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 16:54:47 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6bac0ee823 fix: dynamic list body fallback uses ?: to handle empty string
WhatsApp rejects list messages with empty body.text. Using ?? missed
empty string from config; ?: correctly falls back to default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 16:32:58 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 322aa287dc fix: fetchDynamicList accepts 'datos' key (Palmas360 API convention)
API returns {status,datos:[...]} but bot was looking for {data:[...]},
causing empty rows and dynamic lists never rendering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 16:19:25 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e6dbd5c5bf fix: endpoint test button handles binary (xlsx/pdf) responses correctly
Previously json_encode failed on binary bytes → empty body → JS crash.
Now binary responses show "Archivo recibido: xlsx — 8.7 KB" instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 14:49:53 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e177919be2 fix: resolve {vars} in endpoint test button using saved params config
Fixed vars use their preset (month_start, current_date, etc.); ask-mode
vars fall back to today's date so the test always sends a valid URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 13:50:17 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ff9ff67445 feat: URL var config per endpoint + smart date handling in api_report
- Admin: each endpoint with {vars} shows a vars config section; per-var
  mode is "ask user" (bot prompts in WhatsApp) or "fixed" (preset: today,
  month_start, month_end, last_7, last_30, year_start, or literal)
- Bot: resolves fixed vars before calling endpoint; queues "ask" vars and
  collects them sequentially from the user before executing the report
- date_mode now uses fecha_desde/fecha_hasta (ERP expected names); added
  current_month mode
- JSON responses from ERP (sin_datos etc.) shown as text, not uploaded as file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 13:36:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fb28140e23 docs: API spec for ERP Palmas360 — all 16 endpoints documented
Complete endpoint specification for the ERP team:
- 10 GET (download) endpoints returning PDF/Excel files
- 6 POST (upload) endpoints receiving WhatsApp form data
- Auth headers, request/response schemas, and endpoint_key mapping table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 09:03:01 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 904a1be6ea fix: deduplicate webhook — skip bot response on Meta retry
saveConversation() now returns bool; BotRouter only runs when the
message_id is new (INSERT IGNORE rowCount>0). Prevents WhatsApp
webhook retries from triggering duplicate bot responses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 09:00:50 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 16a03b546d fix: commands clear in-progress state + per_type config rebuilt in DB
Two fixes:
1. Any command (menu, salir, etc.) now clears collecting/__foreach/__cap
   meta before executing, so navigation commands are always a clean reset
   regardless of what flow the user was in.

2. DB per_type was empty ([]); rebuilt with correct structure:
   - Type 1: greeting=submenu_subir, menu/inicio/volver → recibir_info
   - Type 2: greeting=submenu_informes, menu/inicio/volver → descargar_informes
   - Type 3: greeting=show_main_menu, inherits global commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:42:56 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b2abece097 fix: per-type commands extend globals instead of replacing them
Changed commands merge from ?? (replace) to array_merge (extend) so that
per-type overrides for menu/inicio/volver are applied without losing the
global salir, exit, informes, info commands.

Also updated DB config per_type:
- Type 1 (subir info): menu/inicio/volver → recibir_info → submenu_subir
- Type 2 (descargar): menu/inicio/volver → descargar_informes → submenu_informes
- Type 3 (admin): menu → show_main_menu (inherits global)

Each category now lands on its own menu from the first message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:25:15 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 21d30a0895 feat: auto-detect collect_and_post fields from endpoint body_fields
Instead of defining fields inline per flow, the admin now defines them
once on the endpoint (direction=upload). The bot reads body_fields from
company_endpoints at runtime, so the same endpoint can be reused by
multiple collect_and_post flows without duplicating field config.

DB: ALTER TABLE company_endpoints ADD COLUMN body_fields TEXT DEFAULT NULL

Changes:
- NormalBot: falls back to endpoint.body_fields when flow.fields is empty
- DashboardController: endpoint rows show a body_fields section for
  upload endpoints (key / label / prompt per field, add/remove rows)
- companyEndpointSave: reads + validates body_fields JSON and persists it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:10:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3c7ea9fdf9 feat: collect_and_post flow type — sequential field collection + POST
New flow type that asks the user for a configured list of fields one
by one, shows a summary with confirm/cancel, then POSTs all answers
as JSON to the configured endpoint with Bearer auth.

Config example:
  {
    "type": "collect_and_post",
    "endpoint_key": "subir_cosecha",
    "header": "🌿 Ciclos Cosecha",
    "confirm": true,
    "success_text": " Ciclo registrado.",
    "fields": [
      {"key":"fecha",    "label":"Fecha",    "prompt":"📅 ¿Fecha del ciclo? (YYYY-MM-DD)"},
      {"key":"cantidad", "label":"Racimos",  "prompt":"🔢 ¿Cantidad de racimos?"},
      {"key":"finca",    "label":"Finca",    "prompt":"🏡 ¿Nombre de la finca?"}
    ]
  }

POST payload: {fecha, cantidad, finca, telefono, nombre}

Also adds the UI panel in bot-config with dynamic add/remove field rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:06:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 32f87ef27c fix: company save no longer deactivates on partial updates
The checkbox-to-0 logic was running on every save including partial
saves (e.g. bot-config only sends config_json). This silently set
is_active=0 every time the bot config was saved.

Now checkbox defaults only apply when 'name' is present in the data,
which is the case only for full company-form saves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 10:52:44 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fb61a6e027 feat: add collect_for_each flow type to bot-config UI
- Dropdown option '🌧 Registro por ítem' in both PHP-rendered cards
  and the addFlowCard() JS template
- Fields panel: endpoint lista, endpoint upload, campo ID, campo nombre,
  encabezado, pregunta por ítem, mensaje de éxito
- flowTabChange() now shows/hides ftg-foreach panel
- Save handler in index.php parses all flow_fe_* POST fields and
  builds the collect_for_each config object

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 10:40:15 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b45f0fbe51 feat: add collect_for_each flow type for sequential per-item data entry
New flow type that fetches a dynamic list from an endpoint then asks
the user for a value for each item one by one. On completion shows a
summary with confirm/cancel buttons, then POSTs all collected data as
a JSON array to a configured upload endpoint with Bearer + X-API-Key auth.

Usage in flow config:
  {
    "type": "collect_for_each",
    "source_endpoint_key": "fincas_list",
    "endpoint_key": "pluvio_upload",
    "value_field": "id",
    "label_field": "name",
    "question": "¿Cuál es el valor de pluviometría? (mm)",
    "header": "🌧 Pluviometría",
    "success_text": " Pluviometría registrada."
  }

POST payload: [{id, label, valor}, ...]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 10:36:44 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6dd9995b7f fix: new company defaults is_active=true
Creating a company with the Activa checkbox unchecked (or missing)
set is_active=0, silently disabling the bot for that company.
New companies now default to active; existing companies keep their
current value when the form loads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 10:02:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e3be2d5b3f fix: stop repeated/duplicate bot responses
Root cause: WpWebhook called OutboundWorker::processQueue() after every
incoming message. BotRouter already sends synchronously in enqueueResponse();
failed messages stayed status='queued'. On the next incoming message
processQueue() would re-send ALL previously-failed messages, causing
the bot to repeat old responses.

- Remove OutboundWorker::processQueue() from WpWebhook::processEvent()
  BotRouter handles immediate delivery; retries should be manual/cron only
- Fix chatSend(): instead of queuing + processQueue() (which drains ALL
  queued messages), call WhatsAppSender::sendText() directly for the
  one message being sent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 09:05:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cb348bd602 fix: company save — cast int fields, fix error redirect URL
- Cast erp_active_users (and requires_approval, is_active) to int
  before INSERT/UPDATE so empty string '' doesn't fail the INT column
- Fix error redirect for new company: was /admin/companies/new (404),
  now /admin/company/edit (the actual new-company route)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 09:02:34 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b25126e52b fix: company save 500 error — catch DB exceptions, show error to user
- CompanyRepository::save() now catches PDOException and throws a
  RuntimeException with a human-readable message (duplicate name, etc.)
- Also fixes unchecked checkboxes (requires_approval, is_active) not
  being saved as 0 on UPDATE
- index.php save handler catches RuntimeException and redirects back to
  the form with an ?error= param instead of crashing with 500
- companyEdit() renders the error banner when ?error= is present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 18:05:38 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 68317da748 fix: pre-fill phone_number_id on new company from existing shared value
All companies use the same WhatsApp phone number ID. When creating a
new company, auto-fill the field with the value already in use so the
admin doesn't have to look it up and type it manually.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 18:01:13 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1f8af616c5 clarify: phone_number_id hint — shared number is expected
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:58:51 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9ef9eeffcb fix: chat page — readable content, correct unread count, immediate send
- Add extractContent() helper: parses JSON WhatsApp payloads to show
  human-readable text (button titles, body text, captions) instead of
  raw JSON in both the conversation list preview and message bubbles
- Fix unread_count subquery: was comparing outbound_queue.id with
  conversations.id (different sequences); now uses created_at comparison
- Remove broken outbound_queue secondary query from chatMessages():
  all messages (bot + admin) are already in conversations table —
  the extra join caused duplicates and wrong ordering
- chatSend() now calls OutboundWorker::processQueue() immediately after
  queuing so admin messages are sent to WhatsApp without manual trigger

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:54:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2c8b597b28 fix: show general panel by default on company create form
panel-general had class tab-panel (display:none) but showTab('general')
was only called in edit mode — so new company form showed no fields.
Adding active class as default makes it visible immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:49:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 84afeb598c feat: company create with copy-from + fix missing fields
- Add phone_number_id and display_phone fields to create/edit form
  (were never shown or saved despite existing in the DB)
- Add phone_number_id and display_phone to CompanyRepository::save()
- New company form shows "Copiar configuración de empresa" dropdown:
  selecting an existing company pre-fills api_base_url, api_key,
  bot_type, config_json, etc. so admins only need to change
  the name and phone_number_id

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:47:16 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 838cc651b2 fix: repair ErpSync upsert — no duplicate key + preserve config_json
- Added UNIQUE KEY on companies.name in DB so ON DUPLICATE KEY UPDATE
  actually fires instead of always inserting
- Removed config_json and phone_number_id from upsert: those hold
  admin-configured bot data and should never be overwritten by ERP sync
- Skip placeholder API key to avoid sending a fake Bearer token
- Improved error reporting: curl errors, raw response preview, per-company
  error labels

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:44:31 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 332c808bf2 remove: drop Pendientes from nav menu
Pending-approval flow is no longer used. Remove the nav link so the
page is no longer reachable from the sidebar (routes and backend logic
left intact in case they're needed later).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:42:51 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d84f587b52 fix: repair broken loadConvs JS function in chat page
The function had an unclosed arrow function callback (convs.map(c => {)
followed by a misplaced catch, creating a JS syntax error that prevented
the entire <script> block from parsing — so no conversations or messages
rendered at all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:41:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e0c873cb4c fix: define CONFIG and ENDPOINTS in bot-config page for addFlowCard
addFlowCard() referenced CONFIG.menus, CONFIG.flows and ENDPOINTS but
those variables were only defined in the conversation-tree page (a
different route). On /admin/bot-config the access threw ReferenceError,
aborting the function before insertAdjacentHTML ran, so no card appeared.

Compute botConfigJson/botEndpointsJson from already-loaded PHP data and
emit them as const CONFIG / ENDPOINTS at the top of the page's <script>.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:38:28 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4c93c20482 feat: add Google Gemini provider option to bot-config AI tab
- Add Gemini option to ai_provider select (alongside OpenAI/Mock)
- Show/hide OpenAI vs Gemini fields dynamically via aiProviderChange()
- Add gemini_api_key and gemini_model fields to the form
- Add openai_api_key field in bot-config (per-company override)
- Save handler now persists gemini_api_key, gemini_model, openai_api_key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:32:51 -05:00