|
| 1 | +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. |
| 2 | + |
| 3 | +import { definePage } from '@objectstack/spec/ui'; |
| 4 | + |
| 5 | +/** |
| 6 | + * Contact Form — a pure-SDUI DATA-ENTRY page (the data-entry half of SDUI pages). |
| 7 | + * |
| 8 | + * Demonstrates the SDUI form loop end to end, with NO custom code: |
| 9 | + * 1. `variables` declares one page variable per field, each fed by a text |
| 10 | + * input (PageVariableSchema.source = that input's component id). |
| 11 | + * 2. `element:text_input` writes each keystroke into its bound variable |
| 12 | + * (objectui components/src/renderers/basic/text-input.tsx). |
| 13 | + * 3. The submit `element:button` runs an `api` action whose params reference |
| 14 | + * the variables as `{{page.<var>}}`. The console action runtime resolves |
| 15 | + * those tokens against the live page-variable snapshot (published by |
| 16 | + * PageVariableActionBridge) and POSTs the body to the public web-to-lead |
| 17 | + * endpoint, creating a `showcase_inquiry`. |
| 18 | + * |
| 19 | + * POST /api/v1/forms/contact-us/submit (ADR-0056 public form -> showcase_inquiry) |
| 20 | + * |
| 21 | + * This is the showcase analog of a branded cloud onboarding screen — collect a |
| 22 | + * few free-text fields, post them to a backend endpoint — but rendered by the |
| 23 | + * console design system (dark mode, i18n) instead of hand-rolled vanilla HTML. |
| 24 | + */ |
| 25 | +export const ContactFormPage = definePage({ |
| 26 | + name: 'showcase_contact_form', |
| 27 | + label: 'Contact Form (SDUI data entry)', |
| 28 | + icon: 'mail-plus', |
| 29 | + type: 'app', |
| 30 | + template: 'header-sidebar-main', |
| 31 | + isDefault: false, |
| 32 | + // One page variable per field, each written by the text input whose `id` |
| 33 | + // matches `source`. Read back at submit time as `{{page.<name>}}`. |
| 34 | + variables: [ |
| 35 | + { name: 'inquiryName', type: 'string', source: 'field_name' }, |
| 36 | + { name: 'inquiryEmail', type: 'string', source: 'field_email' }, |
| 37 | + { name: 'inquiryCompany', type: 'string', source: 'field_company' }, |
| 38 | + { name: 'inquiryMessage', type: 'string', source: 'field_message' }, |
| 39 | + ], |
| 40 | + regions: [ |
| 41 | + { |
| 42 | + name: 'header', |
| 43 | + width: 'full', |
| 44 | + components: [ |
| 45 | + { |
| 46 | + type: 'page:header', |
| 47 | + properties: { |
| 48 | + title: 'Get in touch', |
| 49 | + subtitle: |
| 50 | + 'A pure-SDUI form — each text input writes a page variable; Submit posts them as one request. No custom code.', |
| 51 | + }, |
| 52 | + }, |
| 53 | + ], |
| 54 | + }, |
| 55 | + { |
| 56 | + name: 'main', |
| 57 | + width: 'large', |
| 58 | + components: [ |
| 59 | + { |
| 60 | + type: 'element:text', |
| 61 | + properties: { |
| 62 | + content: |
| 63 | + 'Tell us about yourself and we’ll reach out. Each field writes a `page.<var>`; the Submit button reads them back as `{{page.<var>}}` and posts them to the web-to-lead endpoint.', |
| 64 | + variant: 'body', |
| 65 | + }, |
| 66 | + }, |
| 67 | + { |
| 68 | + type: 'element:text_input', |
| 69 | + id: 'field_name', |
| 70 | + properties: { label: 'Name', placeholder: 'Ada Lovelace', required: true }, |
| 71 | + }, |
| 72 | + { |
| 73 | + type: 'element:text_input', |
| 74 | + id: 'field_email', |
| 75 | + properties: { |
| 76 | + label: 'Email', |
| 77 | + inputType: 'email', |
| 78 | + placeholder: 'ada@example.com', |
| 79 | + required: true, |
| 80 | + }, |
| 81 | + }, |
| 82 | + { |
| 83 | + type: 'element:text_input', |
| 84 | + id: 'field_company', |
| 85 | + properties: { label: 'Company', placeholder: 'Analytical Engines Ltd.' }, |
| 86 | + }, |
| 87 | + { |
| 88 | + type: 'element:text_input', |
| 89 | + id: 'field_message', |
| 90 | + properties: { label: 'Message', placeholder: 'What can we help with?', required: true }, |
| 91 | + }, |
| 92 | + // Live page-variable feedback — appears the instant an email is typed, |
| 93 | + // proving the input wrote `page.inquiryEmail` (re-evaluated, no reload). |
| 94 | + { |
| 95 | + type: 'element:text', |
| 96 | + id: 'ready_hint', |
| 97 | + visibility: "page.inquiryEmail != ''", |
| 98 | + properties: { |
| 99 | + content: '✓ Looks good — hit Submit to send your inquiry.', |
| 100 | + variant: 'caption', |
| 101 | + }, |
| 102 | + }, |
| 103 | + { type: 'element:divider' }, |
| 104 | + { |
| 105 | + type: 'element:button', |
| 106 | + id: 'submit_inquiry', |
| 107 | + properties: { |
| 108 | + label: 'Submit inquiry', |
| 109 | + variant: 'primary', |
| 110 | + icon: 'send', |
| 111 | + // `api` action -> absolute endpoint. The runtime resolves the |
| 112 | + // `{{page.<var>}}` tokens against the live snapshot before POSTing. |
| 113 | + action: { |
| 114 | + type: 'api', |
| 115 | + target: '/api/v1/forms/contact-us/submit', |
| 116 | + method: 'POST', |
| 117 | + params: { |
| 118 | + name: '{{page.inquiryName}}', |
| 119 | + email: '{{page.inquiryEmail}}', |
| 120 | + company: '{{page.inquiryCompany}}', |
| 121 | + message: '{{page.inquiryMessage}}', |
| 122 | + }, |
| 123 | + successMessage: 'Thanks! We received your inquiry.', |
| 124 | + refreshAfter: false, |
| 125 | + }, |
| 126 | + }, |
| 127 | + }, |
| 128 | + ], |
| 129 | + }, |
| 130 | + ], |
| 131 | +}); |
0 commit comments