victor34593993 commited on
Commit
6318270
·
verified ·
1 Parent(s): d7a9fbf

whatsapp: admin-panel embedded signup prefill

Browse files
app/admin_ui/index.html CHANGED
@@ -784,7 +784,7 @@
784
  var ES = { ready: false, app_id: "", config_id: "", graph_version: "v21.0" };
785
  var esWaba = "", esPhone = "";
786
  async function loadEmbeddedConfig() {
787
- try { ES = await (await api("/whatsapp/embedded-config")).json(); } catch (e) { ES.ready = false; }
788
  var st = $("wa-es-state"), btn = $("wa-es-btn");
789
  if (!st || !btn) return;
790
  if (ES.ready) { st.textContent = atr("Listo: el cliente puede conectar su WhatsApp con un clic."); btn.disabled = false; loadFbSdk(); }
@@ -807,12 +807,18 @@
807
  function launchEmbeddedSignup() {
808
  if (!ES.ready || !window.FB) { toast("Aún no disponible (Meta en revisión)."); return; }
809
  esWaba = ""; esPhone = "";
 
 
 
 
 
 
810
  FB.login(function (resp) {
811
  var code = resp && resp.authResponse && resp.authResponse.code;
812
  if (!code) { toast("Conexión cancelada."); return; }
813
  finishEmbeddedSignup(code);
814
  }, { config_id: ES.config_id, response_type: "code", override_default_response_type: true,
815
- extras: { setup: {}, sessionInfoVersion: "3" } });
816
  }
817
  async function finishEmbeddedSignup(code) {
818
  if (!esWaba || !esPhone) { toast("No recibí el número del cliente; reintenta."); return; }
 
784
  var ES = { ready: false, app_id: "", config_id: "", graph_version: "v21.0" };
785
  var esWaba = "", esPhone = "";
786
  async function loadEmbeddedConfig() {
787
+ try { ES = await (await api("/whatsapp/embedded-config?slug=" + encodeURIComponent(slug()))).json(); } catch (e) { ES.ready = false; }
788
  var st = $("wa-es-state"), btn = $("wa-es-btn");
789
  if (!st || !btn) return;
790
  if (ES.ready) { st.textContent = atr("Listo: el cliente puede conectar su WhatsApp con un clic."); btn.disabled = false; loadFbSdk(); }
 
807
  function launchEmbeddedSignup() {
808
  if (!ES.ready || !window.FB) { toast("Aún no disponible (Meta en revisión)."); return; }
809
  esWaba = ""; esPhone = "";
810
+ var p = ES.prefill || {};
811
+ var setup = {};
812
+ if (p.name) setup.business = Object.assign({}, setup.business, { name: p.name });
813
+ if (p.email) setup.business = Object.assign({}, setup.business, { email: p.email });
814
+ if (p.website) setup.business = Object.assign({}, setup.business, { website: p.website });
815
+ if (p.name) setup.phone = { displayName: p.name };
816
  FB.login(function (resp) {
817
  var code = resp && resp.authResponse && resp.authResponse.code;
818
  if (!code) { toast("Conexión cancelada."); return; }
819
  finishEmbeddedSignup(code);
820
  }, { config_id: ES.config_id, response_type: "code", override_default_response_type: true,
821
+ extras: { setup: setup, sessionInfoVersion: "3" } });
822
  }
823
  async function finishEmbeddedSignup(code) {
824
  if (!esWaba || !esPhone) { toast("No recibí el número del cliente; reintenta."); return; }
app/routes/admin.py CHANGED
@@ -80,6 +80,9 @@ async def db_stats(db: AsyncSession = Depends(get_session)) -> dict:
80
  from sqlalchemy import func
81
  from sqlalchemy import text as sqltext
82
 
 
 
 
83
  from app.models import (
84
  ChatMessage as CM,
85
  )
@@ -104,9 +107,6 @@ async def db_stats(db: AsyncSession = Depends(get_session)) -> dict:
104
  from app.models import (
105
  Tenant as TN,
106
  )
107
- from app.models import (
108
- AbandonedCart as AC,
109
- )
110
 
111
  async def count(model):
112
  return (await db.execute(select(func.count()).select_from(model))).scalar_one()
@@ -274,14 +274,29 @@ async def connect_whatsapp(
274
 
275
 
276
  @router.get("/whatsapp/embedded-config", dependencies=[Depends(require_admin)])
277
- async def whatsapp_embedded_config(settings: Settings = Depends(get_settings)) -> dict:
 
 
 
 
278
  """Frontend reads this to render the 'Conectar WhatsApp' button. `ready` is
279
- False until Meta approves us as a Tech Provider and we set the config id."""
 
 
 
 
 
 
 
 
 
 
280
  return {
281
  "app_id": settings.whatsapp_app_id,
282
  "config_id": settings.whatsapp_config_id,
283
  "graph_version": settings.whatsapp_api_version,
284
  "ready": bool(settings.whatsapp_app_id and settings.whatsapp_config_id),
 
285
  }
286
 
287
 
 
80
  from sqlalchemy import func
81
  from sqlalchemy import text as sqltext
82
 
83
+ from app.models import (
84
+ AbandonedCart as AC,
85
+ )
86
  from app.models import (
87
  ChatMessage as CM,
88
  )
 
107
  from app.models import (
108
  Tenant as TN,
109
  )
 
 
 
110
 
111
  async def count(model):
112
  return (await db.execute(select(func.count()).select_from(model))).scalar_one()
 
274
 
275
 
276
  @router.get("/whatsapp/embedded-config", dependencies=[Depends(require_admin)])
277
+ async def whatsapp_embedded_config(
278
+ slug: str = "",
279
+ db: AsyncSession = Depends(get_session),
280
+ settings: Settings = Depends(get_settings),
281
+ ) -> dict:
282
  """Frontend reads this to render the 'Conectar WhatsApp' button. `ready` is
283
+ False until Meta approves us as a Tech Provider and we set the config id.
284
+ Optional ?slug= (the admin panel passes the tenant currently in view) adds a
285
+ prefill for Meta's 'create new business' screen, same as the client portal."""
286
+ prefill = {"name": "", "email": "", "website": ""}
287
+ if slug:
288
+ tenant = await _require_tenant(db, slug)
289
+ prefill = {
290
+ "name": (tenant.brand_name or "").strip()[:100],
291
+ "email": (tenant.support_email or "").strip(),
292
+ "website": (f"https://{tenant.shopify_shop}" if tenant.shopify_shop else ""),
293
+ }
294
  return {
295
  "app_id": settings.whatsapp_app_id,
296
  "config_id": settings.whatsapp_config_id,
297
  "graph_version": settings.whatsapp_api_version,
298
  "ready": bool(settings.whatsapp_app_id and settings.whatsapp_config_id),
299
+ "prefill": prefill,
300
  }
301
 
302
 
tests/routes/test_admin.py CHANGED
@@ -315,3 +315,32 @@ async def test_embed_product_image(app_client, db_session, monkeypatch):
315
  r2 = await client.post("/admin/tenants/imgt/products/embed-image", headers=AUTH,
316
  json={"ref": "NoExiste", "image_url": "http://x/y.jpg"})
317
  assert r2.status_code == 404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  r2 = await client.post("/admin/tenants/imgt/products/embed-image", headers=AUTH,
316
  json={"ref": "NoExiste", "image_url": "http://x/y.jpg"})
317
  assert r2.status_code == 404
318
+
319
+
320
+ async def test_whatsapp_embedded_config_prefills_from_tenant_slug(app_client, db_session):
321
+ """The admin panel passes ?slug=<tenant currently in view> so Meta's Embedded
322
+ Signup 'create new business' screen prefills that tenant's name/email/website
323
+ (fewer manual fields when Victor connects a client's WhatsApp on their behalf)."""
324
+ from sqlalchemy import select
325
+
326
+ from app.models import Tenant
327
+
328
+ _app, client = app_client
329
+ await client.post("/admin/tenants", headers=AUTH, json={"slug": "wapre", "name": "X"})
330
+ t = (await db_session.execute(select(Tenant).where(Tenant.slug == "wapre"))).scalar_one()
331
+ t.brand_name = "Tienda Pre"
332
+ t.support_email = "dueno@tiendapre.com"
333
+ t.shopify_shop = "tiendapre.myshopify.com"
334
+ await db_session.commit()
335
+
336
+ r = await client.get("/admin/whatsapp/embedded-config?slug=wapre", headers=AUTH)
337
+ assert r.status_code == 200
338
+ assert r.json()["prefill"] == {
339
+ "name": "Tienda Pre", "email": "dueno@tiendapre.com",
340
+ "website": "https://tiendapre.myshopify.com",
341
+ }
342
+
343
+ # no slug -> empty prefill, back-compat with the previous (tenant-agnostic) shape
344
+ r2 = await client.get("/admin/whatsapp/embedded-config", headers=AUTH)
345
+ assert r2.status_code == 200
346
+ assert r2.json()["prefill"] == {"name": "", "email": "", "website": ""}