page.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import Link from "next/link";
  2. import { Code } from "@/components/code";
  3. export const metadata = {
  4. title: "Actions | json-render",
  5. };
  6. export default function ActionsPage() {
  7. return (
  8. <article>
  9. <h1 className="text-3xl font-bold mb-4">Actions</h1>
  10. <p className="text-muted-foreground mb-8">
  11. Handle user interactions safely with named actions.
  12. </p>
  13. <h2 className="text-xl font-semibold mt-12 mb-4">Why Named Actions?</h2>
  14. <p className="text-sm text-muted-foreground mb-4">
  15. Instead of AI generating arbitrary code, it declares <em>intent</em> by name.
  16. Your application provides the implementation. This is a core guardrail.
  17. </p>
  18. <h2 className="text-xl font-semibold mt-12 mb-4">Defining Actions</h2>
  19. <p className="text-sm text-muted-foreground mb-4">
  20. Define available actions in your catalog:
  21. </p>
  22. <Code lang="typescript">{`const catalog = createCatalog({
  23. components: { /* ... */ },
  24. actions: {
  25. submit_form: {
  26. params: z.object({
  27. formId: z.string(),
  28. }),
  29. description: 'Submit a form',
  30. },
  31. export_data: {
  32. params: z.object({
  33. format: z.enum(['csv', 'pdf', 'json']),
  34. filters: z.object({
  35. dateRange: z.string().optional(),
  36. }).optional(),
  37. }),
  38. },
  39. navigate: {
  40. params: z.object({
  41. url: z.string(),
  42. }),
  43. },
  44. },
  45. });`}</Code>
  46. <h2 className="text-xl font-semibold mt-12 mb-4">ActionProvider</h2>
  47. <p className="text-sm text-muted-foreground mb-4">
  48. Provide action handlers to your app:
  49. </p>
  50. <Code lang="tsx">{`import { ActionProvider } from '@json-render/react';
  51. function App() {
  52. const handlers = {
  53. submit_form: async (params) => {
  54. const response = await fetch('/api/submit', {
  55. method: 'POST',
  56. body: JSON.stringify({ formId: params.formId }),
  57. });
  58. return response.json();
  59. },
  60. export_data: async (params) => {
  61. const blob = await generateExport(params.format, params.filters);
  62. downloadBlob(blob, \`export.\${params.format}\`);
  63. },
  64. navigate: (params) => {
  65. window.location.href = params.url;
  66. },
  67. };
  68. return (
  69. <ActionProvider handlers={handlers}>
  70. {/* Your UI */}
  71. </ActionProvider>
  72. );
  73. }`}</Code>
  74. <h2 className="text-xl font-semibold mt-12 mb-4">Using Actions in Components</h2>
  75. <Code lang="tsx">{`const Button = ({ element, onAction }) => (
  76. <button onClick={() => onAction(element.props.action, {})}>
  77. {element.props.label}
  78. </button>
  79. );
  80. // Or use the useAction hook
  81. import { useAction } from '@json-render/react';
  82. function SubmitButton() {
  83. const submitForm = useAction('submit_form');
  84. return (
  85. <button onClick={() => submitForm({ formId: 'contact' })}>
  86. Submit
  87. </button>
  88. );
  89. }`}</Code>
  90. <h2 className="text-xl font-semibold mt-12 mb-4">Actions with Confirmation</h2>
  91. <p className="text-sm text-muted-foreground mb-4">
  92. AI can declare actions that require user confirmation:
  93. </p>
  94. <Code lang="json">{`{
  95. "type": "Button",
  96. "props": {
  97. "label": "Delete Account",
  98. "action": {
  99. "name": "delete_account",
  100. "params": { "userId": "123" },
  101. "confirm": {
  102. "title": "Delete Account?",
  103. "message": "This action cannot be undone.",
  104. "variant": "danger"
  105. }
  106. }
  107. }
  108. }`}</Code>
  109. <h2 className="text-xl font-semibold mt-12 mb-4">Action Callbacks</h2>
  110. <p className="text-sm text-muted-foreground mb-4">
  111. Handle success and error states:
  112. </p>
  113. <Code lang="json">{`{
  114. "type": "Button",
  115. "props": {
  116. "label": "Save",
  117. "action": {
  118. "name": "save_changes",
  119. "params": { "documentId": "doc-1" },
  120. "onSuccess": {
  121. "set": { "/ui/savedMessage": "Changes saved!" }
  122. },
  123. "onError": {
  124. "set": { "/ui/errorMessage": "$error.message" }
  125. }
  126. }
  127. }
  128. }`}</Code>
  129. <h2 className="text-xl font-semibold mt-12 mb-4">Next</h2>
  130. <p className="text-sm text-muted-foreground">
  131. Learn about <Link href="/docs/visibility" className="text-foreground hover:underline">conditional visibility</Link>.
  132. </p>
  133. </article>
  134. );
  135. }