Shopify Customer Account Extensions — OrigenVerde

Extensiones de cuenta de cliente con Polaris Web Components (s- prefix) · Preact · API 2026-01

customer-account.profile.block.render customer-account.order-index.block.render Polaris 2026-01
LoyaltyBlock.tsx
SubscriptionBlock.tsx
shopify.extension.toml
// customer-account.profile.block.render
// Target: Perfil del cliente — Bloque de puntos de fidelidad
// OrigenVerde · CULTIVA IA

import "@shopify/ui-extensions/preact";
import { render } from "preact";
import {
  useApi,
  useCustomer,
  useMetafield,
  useState,
} from "@shopify/ui-extensions-react/customer-account";

interface LoyaltyData {
  points: number;
  level: "basico" | "plus" | "premium";
}

const LEVELS = {
  basico: { min: 0, max: 2499, label: "Verde Básico", icon: "🌱" },
  plus:   { min: 2500, max: 4999, label: "Verde Plus", icon: "🌿" },
  premium: { min: 5000, max: Infinity, label: "Verde Premium", icon: "🍀" },
};

function getLevel(pts: number): keyof typeof LEVELS {
  if (pts >= 5000) return "premium";
  if (pts >= 2500) return "plus";
  return "basico";
}

function LoyaltyBlock() {
  const [modalOpen, setModalOpen] = useState(false);

  // Leer puntos del metafield custom del cliente
  const pointsMeta = useMetafield({
    namespace: "origenverde",
    key:       "loyalty_points",
  });

  const points = parseInt(pointsMeta?.value ?? "0", 10);
  const levelKey = getLevel(points);
  const level    = LEVELS[levelKey];
  const nextLevel= levelKey === "plus" ? LEVELS.premium : null;
  const progress = nextLevel
    ? Math.round(((points - level.min) / (nextLevel.min - level.min)) * 100)
    : 100;

  return (
    <s-section heading="Mis Puntos Verdes">

      {/* Nivel actual con avatar */}
      <s-stack direction="inline" gap="base" align-items="center">
        <s-avatar initials="LG" size="large" alt="Laura García" />
        <s-stack direction="block" gap="tight">
          <s-text type="strong">{level.icon} {level.label}</s-text>
          <s-badge tone="success">Cliente Fiel</s-badge>
        </s-stack>
      </s-stack>

      {/* Puntos */}
      <s-box padding="base" background="subdued" border-radius="base">
        <s-stack direction="inline" align-items="center"
                 gap="loose">
          <s-text tone="success">Puntos acumulados</s-text>
          <s-text type="strong" tone="success">
            {points.toLocaleString("es-ES")} pts
          </s-text>
        </s-stack>
      </s-box>

      {/* Progreso hacia Premium */}
      {nextLevel && (
        <s-stack direction="block" gap="tight">
          <s-stack direction="inline" gap="loose">
            <s-text>Hacia Verde Premium</s-text>
            <s-text>{progress}%</s-text>
          </s-stack>
          <s-progress
            value={progress}
            max={100}
            tone="auto"
            accessibility-label="Progreso hacia Verde Premium"
          />
        </s-stack>
      )}

      {/* Botón canjear */}
      <s-button-group>
        <s-button variant="primary"
          command-for="redeem-modal">
          Canjear puntos
        </s-button>
        <s-button variant="secondary">Ver historial</s-button>
      </s-button-group>

      {/* Modal de canje */}
      <s-modal id="redeem-modal"
               heading="Canjear Puntos Verdes">
        <s-choice-list label="Selecciona tu descuento"
                        name="redeem_option">
          <s-choice value="500">500 pts → 5€ descuento</s-choice>
          <s-choice value="1000">1.000 pts → 12€ descuento</s-choice>
          <s-choice value="2000">2.000 pts → Envío gratis 3 pedidos</s-choice>
        </s-choice-list>
        <s-button variant="primary">Confirmar canje</s-button>
      </s-modal>

    </s-section>
  );
}

render("customer-account.profile.block.render",
  () => <LoyaltyBlock />);
shopify.extension.toml
api_version = "2026-01"

[[extensions]]
type   = "ui_extension"
name   = "origenverde-loyalty-block"
handle = "origenverde-loyalty"

[[extensions.targeting]]
module = "./src/LoyaltyBlock.tsx"
target = "customer-account.profile.block.render"

[[extensions]]
type   = "ui_extension"
name   = "origenverde-subscription-block"
handle = "origenverde-subscription"

[[extensions.targeting]]
module = "./src/SubscriptionBlock.tsx"
target = "customer-account.order-index.block.render"
origenverde.es/account
Vista previa extensiones
Cliente
OrigenVerde · origenverde.es
API Version
Shopify 2026-01
Targets
profile.block.render + order-index.block.render
Stack
Preact + Polaris Web Components