@qorus-test3/site-components
v0.1.9
Published
Reusable React components for Qorus sites
Readme
@qorus/site-components
Librería piloto de componentes React reutilizables para páginas Qorus/Base44. No incluye router, autenticación de producto ni prerender.
Uso
npm install
npm run devEl segundo comando abre el playground con los tres componentes y un submit simulado. Para consumir la librería:
import {
QorusSiteProvider,
QorusItemList,
QorusAppointment,
QorusContactForm,
} from "@qorus/site-components";
const appConfig = {
publicHost: import.meta.env.VITE_PXP_PUBLIC_HOST,
// Puede ser la URL temporal de Base44 o del ambiente de pruebas.
siteUrl: "https://profile.9jul6-dev.qorus.io",
};
<QorusSiteProvider
config={appConfig}
>
<QorusItemList title="Portfolio" items={items} />
<QorusAppointment label="Reservar cita" />
<QorusContactForm />
</QorusSiteProvider>;QorusSiteProvider acepta esas claves mediante config o como props directas,
además de submitPath, siteLookupPath y submitAdapter.
La URL del sitio se resuelve en este orden:
config.siteUrl, para Base44, sandboxes y pruebas manuales;VITE_SITE_URL, para el pipeline de prerender;window.location.href, únicamente después de montar en el navegador;""en Node si no existe ninguno de los anteriores.
El bundle ESM preserva literalmente import.meta.env, para que estas variables
sean resueltas por el build Vite de la aplicación consumidora y no queden
congeladas al momento de publicar @qorus-test3/site-components.
La detección del navegador se difiere hasta useEffect, por lo que el primer
render del cliente coincide con el HTML generado en Node y no causa errores de
hidratación. En prerender, los efectos de red y la inyección del script de citas
no se ejecutan.
Cuando hay una URL resuelta, el provider abre una sesión PKCE del API Gateway
(POST /Public/session → POST /Public/token) y recién entonces consulta
DEFAULT_SITE_LOOKUP_PATH?baseUrl=<siteUrl> con Authorization: Bearer.
La sesión inicial usa gatewayEmbedId (o el embed de onboarding por defecto).
La respuesta alimenta siteId, vendorId, locationId, baseUrl, site y
env. Los embeds quedan separados de forma explícita:
gatewayEmbedId: sesión PKCE del API Gateway (EMBED_ID_QORD_PROFILE, oconfig.gatewayEmbedId/config.embedId/VITE_ONBOARDING_EMBED_ID).embedIdse mantiene como alias de este valor para compatibilidad.appointmentEmbedId: widget de citas (EMBED_ID_APPOINTMENT_WIDGET). Nunca cae al embed del gateway: si el lookup no lo trae, usaap_de8a96b911fddd94ad9f9e0bc6ee4734_mrdjnamn.formEmbedId: metadata del formulario (EMBED_ID_QORD_BASIC_FORM); la sesión del submit usagatewayEmbedId, no este valor.
Cada componente usa el identificador que le corresponde. El provider aplica de
inmediato los fallbacks siteId 1708, vendorId "1538" y locationId 75
para no bloquear la UI; si el lookup llega, los reemplaza. Si falla, esos
valores se mantienen y el error queda en useQorusSite().error.
Para uso directo del SDK, getSiteByBaseUrl(options) es la variante segura:
retorna null si falta URL o si falla sesión/red. La función estricta
getSiteConfigByBaseUrl(options) conserva errores para consumidores que
necesitan manejarlos explícitamente.
Si el gateway de ese sitio requiere otro embed, embedIdEnvKey permite indicar
la clave de env, por ejemplo "EMBED_ID_PROFILE". Los valores explícitos en
props del componente conservan la máxima prioridad. El provider también acepta
locationImpl, siteLookupClient y fetchImpl para pruebas. El playground
muestra el patrón de variables en playground/src/appConfig.js.
Componentes
QorusItemList
title,subtitle: encabezado opcional.visible: al serfalseno renderiza nada.preset:"promotions"|"members"|"packages". Usa el endpoint real, el método y losdefaultParamsdeITEM_PRESETS. Los overrides de props (limit,_vendorId,_locationId,locationId,params, …) se combinan encima de esos defaults.endpoint/method: sustituyen el path o el verbo del preset.items: array o respuesta envolvente ({ data },{ result }) cuando no hay fetch.autoMapListextrae la lista y, si haypreset, aplica el mapeo unificado.- Los presets aceptan las dos convenciones de Qorus para un mismo campo:
packageNameoPACKAGE_NAME,promoCodeoPROMO_CODE, etc. El precio se busca enprice,amount,PACKAGE_PRICEy variantes; si el endpoint no devuelve precio, la tarjeta omite esa línea en vez de quedar vacía. - Sin
fieldsnirenderItem, cada tarjeta usa las propiedades normalizadastitle,subtitle,badge,dateyprice. renderItem={(item) => …}ofields={["title", "raw.email"]}permiten leer cualquier campo original enitem.raw[fieldName].className,itemClassName: clases para que el sitio aplique su diseño.
Los ítems con order se ordenan primero, de menor a mayor. La lista conserva
HTML semántico (section, ul, li, article).
Las herramientas no-code pueden leer
@qorus/site-components/schemas/QorusItemList.schema.json.
<QorusItemList
title="Promotions"
preset="promotions"
_vendorId="19"
limit={10}
/>;
<QorusItemList
title="Members"
preset="members"
renderItem={(item) => (
<article>
<h3>{item.title}</h3>
<p>{item.raw.email}</p>
</article>
)}
/>;QorusAppointment
Renderiza un button, no un enlace. Props principales: label, onTrigger,
widgetClass (default appointment-widget), eventName (default
qorus:appointment-trigger), className, disabled, siteId y embedId.
La URL del script y la clase salen de APP_CONFIG: primero
site.config.APPOINTMENT_WIDGET_SCRIPT_URL /
APPOINTMENT_WIDGET_DEFAULT_CLASS, luego VITE_APPOINTMENT_WIDGET_SCRIPT_URL,
y por último
https://widgets-dev.qorus.io/appointment/appointment-widget.js y
appointment-widget. El botón incluye data-qorus-widget="appointment",
data-site-id (fallback 1708), data-embed-id y data-vendor-id.
data-embed-id usa appointmentEmbedId (nunca el embed del gateway):
prop embedId → site.config.appointmentEmbedId (EMBED_ID_APPOINTMENT_WIDGET)
→ ap_de8a96b911fddd94ad9f9e0bc6ee4734_mrdjnamn. Ese mismo valor viaja como
config.embedId en onTrigger / el CustomEvent, que es lo que el widget
manda a getProviderDetails.
vendorId se resuelve: prop → site.config.vendorId → VITE_VENDOR_ID →
"1538".
El host puede integrar el widget de dos formas:
<QorusAppointment
label="Agendar"
onTrigger={({ config, widgetClass, event }) => openWidget(config)}
/>;Si no se pasa onTrigger, el botón emite en window un CustomEvent con el
nombre de eventName; su detail contiene { config, widgetClass,
nativeEvent }. También conserva widgetClass como clase CSS para scripts
existentes que escuchan clicks delegados. El playground pasa un callback y
muestra un stub visible.
QorusContactForm
Por defecto incluye name, email, phone y message. El teléfono se
formatea mientras se escribe como xxx-xxx-xxxx (hasta 10 dígitos) y usa
placeholder/pattern nativos para ese formato. fields acepta
objetos con name, label, type (text, email, tel, textarea o
select), required, placeholder, autoComplete, rows, options,
defaultValue e inputProps.
También acepta defaultValues, submitLabel, source, pageUrl,
submitAdapter, onSuccess, onError y textos de éxito/error. className y
slots permiten controlar el look sin imponerlo:
<QorusContactForm
fields={[
{ name: "fullName", label: "Nombre", required: true },
{ name: "topic", label: "Tema", type: "select", options: ["Ventas", "Otro"] },
]}
slots={{
form: "form",
field: "field",
label: "label",
input: "input",
actions: "actions",
submit: "button",
status: "status",
}}
/>;El estado mínimo incluye loading, success y error.
Flujo real de envío
createQorusSubmitAdapter implementa el wiring real:
- crea PKCE y hace
POST /Public/sessionconconfig.gatewayEmbedId(no conappointmentEmbedId); - intercambia el verifier con
POST /Public/token; - resuelve un
siteIdentero positivo desdefields.siteId,config.siteId,VITE_SITE_IDo el fallback1708, en ese orden; - hace
POST /api/hq-nd/ContentType/submitOpenHouseOptincon{ siteId, ...fields }, preservando todos los campos del formulario.
Las llamadas autenticadas llevan el bearer token del gateway. Se puede
inyectar fetchImpl, cryptoImpl o un client para pruebas. El adapter mock
del playground permite probar la UI sin credenciales; quitándolo del provider
se activa el adapter real. Las APIs de navegador se consultan sólo al disparar
el flujo, no durante importación o render.
Build
npm run build
npm run build:playgroundReact y React DOM son peerDependencies; no se empaquetan en la librería.
Datos para agentes
useQorusData({ endpoint, method, params, preset, siteConfig }) (o la forma
corta useQorusData(endpoint, siteConfig)) aisla PKCE/gateway y devuelve
{ items, loading, error } ya normalizados. autoMapList(response, preset)
desenvuelve { data } / { result } / arrays y mapea promotions, members o
packages a title, subtitle, badge, date, price y raw.
