Paso 1 · Búsqueda en documentación — scripts/search_docs.mjs "admin action modal order"
polaris-admin-extensions/docs/admin-action.md · línea 42
Un AdminAction se monta en el menú contextual del recurso. Requiere target (p. ej. admin.order-details.action.render) y un componente React que recibe api.data con el contexto del recurso actual.
polaris-admin-extensions/docs/components/Modal.md · línea 8
Usa <Modal> de @shopify/admin-ui-extensions-react. La prop es open (boolean), no isOpen. Cierre con onClose.
polaris-admin-extensions/docs/api/resource-context.md · línea 19
Acceso al ID del pedido actual: api.data.orderId (string). Disponible solo en targets admin.order-*.
polaris-admin-extensions/examples/action-with-modal.tsx · línea 1
Ejemplo completo de AdminAction con Modal confirmatorio. Usa useAppBridge para dispatch y Redirect para navegar tras acción.
Revisión 1 — Fallo de validación detectado ⚡ Retry 1/3
ValidationError: Invalid prop `isOpen` on component `Modal`.
Expected prop `open` (boolean). Received `isOpen` (boolean).
at AdminExtension → OrderApprovalModal
Artifact: admin-ext-order-approval-7f3a · revision 1
Hint: search_docs.mjs "Modal props" for correct API signature.
Fix aplicado: Renombrado isOpen={showModal} → open={showModal} según la documentación encontrada en el Paso 3b (search: "Modal props"). Revision incrementada a 2.
Código final · Validado en revision 2
import React, { useState } from 'react';
import { extend, AdminAction } from '@shopify/admin-ui-extensions';
import {
Button, Modal, Stack, Text, Badge, Banner
} from '@shopify/admin-ui-extensions-react';
// Target: aparece en el menú de acción de cada pedido
extend('admin.order-details.action.render', (root, api) => {
const app = render(<OrderApprovalAction api={api} />, root);
});
function OrderApprovalAction({ api }) {
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [done, setDone] = useState(false);
const orderId = api.data.orderId; // ej. "gid://shopify/Order/5512384299"
async function handleApprove() {
setLoading(true);
await fetch(`/apps/approvals/orders/${orderId}/approve`, {
method: 'POST',
});
setLoading(false);
setDone(true);
setOpen(false);
}
return (
<>
<AdminAction
primaryAction={<Button onClick={() => setOpen(true)}>Aprobar pedido</Button>}
>
{done && <Banner status="success">Pedido aprobado</Banner>}
</AdminAction>
<Modal
open={open}
title="Confirmar aprobación"
onClose={() => setOpen(false)}
primaryAction={{
content: loading ? "Aprobando…" : "Confirmar",
onAction: handleApprove,
loading: loading,
}}
secondaryActions={[{
content: "Cancelar",
onAction: () => setOpen(false),
}]}
>
<Stack vertical spacing="base">
<Text>¿Confirmas la aprobación manual del pedido</Text>
<Badge status="info">{orderId}</Badge>
<Text color="subdued">Esta acción no se puede deshacer.</Text>
</Stack>
</Modal>
</>
);
}
✓ scripts/validate.mjs — Resultado (revision 2)
--model
claude-sonnet-4-6
--client-name
claude-code
--artifact-id
admin-ext-order-approval-7f3a
--revision
2
target válido
admin.order-details.action.render ✓
props Modal
open (boolean) ✓
api.data.orderId
acceso correcto ✓
imports
@shopify/admin-ui-extensions-react ✓
resultado
PASS — listo para entregar