diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/.gitignore b/examples/nuxt/nuxt-headlesswp-gravity-forms/.gitignore new file mode 100644 index 0000000..4a7f73a --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/.gitignore @@ -0,0 +1,24 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/README.md b/examples/nuxt/nuxt-headlesswp-gravity-forms/README.md new file mode 100644 index 0000000..0287a36 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/README.md @@ -0,0 +1,73 @@ +# Nuxt 3 Headless WordPress with Gravity Forms + +This project demonstrates how to integrate Nuxt 3 with WordPress as a headless CMS, specifically focusing on Gravity Forms integration using WPGraphQL and WPGraphQL for Gravity Forms. + +## Project Overview + +This project includes several demo routes: + +- `/headlesswp-gform`: Main demo showcasing Gravity Forms integration with WPGraphQL +- `/products`: Test route using a dummy REST API (for API integration testing) +- `/wpblog`: Test route demonstrating native WordPress post data fetching + +## Prerequisites + +- WordPress installation with: + - WPGraphQL plugin + - WPGraphQL for Gravity Forms plugin + - Gravity Forms plugin +- Nuxt 3 + +## Setup + +Make sure to install dependencies: + +```bash +# npm +npm install + + +``` + +## Development Server + +Start the development server on `http://localhost:3000`: + +```bash +# npm +npm run dev + + +``` + +## Environment Variables + +Create a `.env` file with: + +```bash +WORDPRESS_URL= +``` + +## Production + +Build the application for production: + +```bash +# npm +npm run build + + +``` + +Locally preview production build: + +```bash +# npm +npm run preview + + + + +``` + +Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/assets/css/tailwind.css b/examples/nuxt/nuxt-headlesswp-gravity-forms/assets/css/tailwind.css new file mode 100644 index 0000000..1565f44 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/assets/css/tailwind.css @@ -0,0 +1,16 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + @apply bg-gray-50; +} + +@layer components { + .btn { + @apply bg-[#12b488] text-white px-3 py-2 rounded-md text-sm text-white inline-block; + } + .card { + @apply p-3 rounded-md bg-white shadow-md h-full; + } +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/AddressField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/AddressField.vue new file mode 100644 index 0000000..db2037d --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/AddressField.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/ChoiceListField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/ChoiceListField.vue new file mode 100644 index 0000000..22bbaf5 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/ChoiceListField.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/DateField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/DateField.vue new file mode 100644 index 0000000..cbe735a --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/DateField.vue @@ -0,0 +1,39 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/DropdownField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/DropdownField.vue new file mode 100644 index 0000000..3a5e62c --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/DropdownField.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/EmailField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/EmailField.vue new file mode 100644 index 0000000..fbf1988 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/EmailField.vue @@ -0,0 +1,54 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/InputField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/InputField.vue new file mode 100644 index 0000000..98be933 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/InputField.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/NameField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/NameField.vue new file mode 100644 index 0000000..673d069 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/NameField.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/PhoneField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/PhoneField.vue new file mode 100644 index 0000000..8d332ac --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/PhoneField.vue @@ -0,0 +1,37 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/TimeField.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/TimeField.vue new file mode 100644 index 0000000..8852241 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/TimeField.vue @@ -0,0 +1,38 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/index.js b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/index.js new file mode 100644 index 0000000..f4493c9 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/form-fields/index.js @@ -0,0 +1,9 @@ +export { default as InputField } from "./InputField.vue"; +export { default as EmailField } from "./EmailField.vue"; +export { default as DropdownField } from "./DropdownField.vue"; +export { default as ChoiceListField } from "./ChoiceListField.vue"; +export { default as AddressField } from "./AddressField.vue"; +export { default as DateField } from "./DateField.vue"; +export { default as TimeField } from "./TimeField.vue"; +export { default as NameField } from "./NameField.vue"; +export { default as PhoneField } from "./PhoneField.vue"; diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/product/ProductCard.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/product/ProductCard.vue new file mode 100644 index 0000000..8763e67 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/product/ProductCard.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/product/ProductDetails.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/product/ProductDetails.vue new file mode 100644 index 0000000..1d4139e --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/product/ProductDetails.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/wp/Post.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/wp/Post.vue new file mode 100644 index 0000000..d51f4f5 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/wp/Post.vue @@ -0,0 +1,18 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/components/wp/TheHeader.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/wp/TheHeader.vue new file mode 100644 index 0000000..88829ff --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/components/wp/TheHeader.vue @@ -0,0 +1,5 @@ + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/composables/useFormFields.js b/examples/nuxt/nuxt-headlesswp-gravity-forms/composables/useFormFields.js new file mode 100644 index 0000000..a8ab0dd --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/composables/useFormFields.js @@ -0,0 +1,52 @@ +import { defineAsyncComponent } from "vue"; + +export const useFormFields = () => { + const loggedTypes = new Set(); + + /** + * Resolves the Vue component for a given field based on its inputType. + * If inputType is not present, falls back to using type. + * + * @param {Object} field - The Gravity Form field object. + * @returns {Component|null} The async Vue component for this field. + */ + const resolveFieldComponent = (field) => { + const fieldType = field.inputType + ? field.inputType.toUpperCase() + : field.type.toUpperCase(); + + const typeToComponent = { + ADDRESS: "AddressField", + TEXT: "InputField", + TEXTAREA: "InputField", + EMAIL: "EmailField", + NAME: "NameField", + PHONE: "PhoneField", + SELECT: "DropdownField", + MULTISELECT: "DropdownField", + CHECKBOX: "ChoiceListField", + RADIO: "ChoiceListField", + DATE: "DateField", + TIME: "TimeField", + WEBSITE: "InputField", + // Add any additional mappings if needed. + }; + + // Log the field type for debugging on the first occurrence. + if (!loggedTypes.has(fieldType)) { + console.log("Mapping field type:", fieldType); + loggedTypes.add(fieldType); + } + + const componentName = typeToComponent[fieldType]; + return componentName + ? defineAsyncComponent(() => + import(`~/components/form-fields/${componentName}.vue`) + ) + : null; + }; + + return { + resolveFieldComponent, + }; +}; diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/composables/useGravityForm.js b/examples/nuxt/nuxt-headlesswp-gravity-forms/composables/useGravityForm.js new file mode 100644 index 0000000..35c27e3 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/composables/useGravityForm.js @@ -0,0 +1,236 @@ +import { ref } from "vue"; +import { useRuntimeConfig } from "#app"; + +export default function useGravityForm() { + const config = useRuntimeConfig(); + const formFields = ref([]); + + const formQuery = ` + query GetGravityForm($formId: ID!) { + gfForm(id: $formId, idType: DATABASE_ID) { + formFields(first: 300) { + nodes { + id + databaseId + inputType + type + visibility + ... on GfFieldWithLabelSetting { + label + } + ... on GfFieldWithRulesSetting { + isRequired + } + ... on GfFieldWithCssClassSetting { + cssClass + } + ... on GfFieldWithDefaultValueSetting { + defaultValue + } + ... on GfFieldWithSizeSetting { + size + } + ... on GfFieldWithPlaceholderSetting { + placeholder + } + ... on GfFieldWithMaxLengthSetting { + maxLength + } + ... on GfFieldWithInputMaskSetting { + inputMaskValue + } + ... on GfFieldWithChoicesSetting { + choices { + text + value + } + inputs { + id + label + } + } + ... on GfFieldWithConditionalLogicSetting { + conditionalLogic { + actionType + logicType + rules { + fieldId + operator + value + } + } + } + } + } + } +} + + + `; + + const fetchForm = () => { + console.log("Making GraphQL request:", { + url: config.public.wordpressUrl, + query: formQuery, + }); + + const { data, status, fetchError, execute, refresh } = useFetch( + config.public.wordpressUrl, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + query: formQuery, + variables: { formId: "1" }, + }), + immediate: false, + transform: (res) => { + if (res.errors) { + console.error("GraphQL Errors:", res.errors); + throw new Error(res.errors[0].message); + } + const fields = res.data?.gfForm?.formFields?.nodes; + if (!Array.isArray(fields)) { + console.error("Invalid fields data:", res.data); + throw new Error("Invalid form fields data"); + } + return fields; + }, + } + ); + + return { data, status, fetchError, execute, refresh }; + }; + + const transformFieldValue = (field, value) => { + if (!field) return null; + const fieldId = parseInt(field.databaseId, 10); + switch (field.type) { + case "CHECKBOX": + if (!Array.isArray(value) || !value.length) return null; + return { + id: fieldId, + checkboxValues: value.map((val, index) => ({ + inputId: parseFloat(`${fieldId}.${index + 1}`), + value: val, + })), + }; + case "ADDRESS": + return { + id: fieldId, + addressValues: { + street: value?.street || "", + lineTwo: value?.lineTwo || "", + city: value?.city || "", + state: value?.state || "", + zip: value?.zip || "", + country: value?.country || "US", + }, + }; + case "EMAIL": + return { + id: fieldId, + emailValues: { + value: value || "", + confirmationValue: value || "", + }, + }; + case "NAME": + return { + id: fieldId, + nameValues: { + prefix: value?.prefix || "", + first: value?.first || "", + middle: value?.middle || "", + last: value?.last || "", + suffix: value?.suffix || "", + }, + }; + case "MULTISELECT": + case "POST_CATEGORY": + case "POST_CUSTOM": + case "POST_TAGS": + return { + id: fieldId, + values: Array.isArray(value) ? value : [], + }; + default: + return { + id: fieldId, + value: value?.toString() || "", + }; + } + }; + + const submitForm = async (formId, fieldValues) => { + try { + const transformedValues = Object.entries(fieldValues) + .map(([id, value]) => { + const field = formFields.value.find( + (f) => f.databaseId === parseInt(id, 10) + ); + if (!field) { + console.warn(`No field found for ID ${id}`); + return null; + } + return transformFieldValue(field, value); + }) + .filter(Boolean); + + const mutation = ` + mutation SubmitForm($formId: ID!, $fieldValues: [FormFieldValuesInput!]!) { + submitGfForm(input: { + id: $formId + fieldValues: $fieldValues + }) { + errors { + id + message + } + confirmation { + message + type + } + entry { + id + ... on GfSubmittedEntry { + databaseId + } + } + } + } + `; + + const response = await fetch(config.public.wordpressUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + query: mutation, + variables: { + formId: parseInt(formId, 10), + fieldValues: transformedValues, + }, + }), + }); + + const result = await response.json(); + + if (result.errors) { + throw new Error(result.errors.map((e) => e.message).join(", ")); + } + + return result.data.submitGfForm; + } catch (error) { + console.error("Submit form error:", error); + throw error; + } + }; + + return { formFields, fetchForm, submitForm }; +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/gforms-questionnaire.json b/examples/nuxt/nuxt-headlesswp-gravity-forms/gforms-questionnaire.json new file mode 100644 index 0000000..399a1f8 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/gforms-questionnaire.json @@ -0,0 +1,927 @@ +{ + "0": { + "title": "Questionnaire", + "description": "We want to know all about you! Please answer the questions below.", + "labelPlacement": "top_label", + "descriptionPlacement": "below", + "button": { + "type": "text", + "text": "Submit Questionnaire", + "imageUrl": "" + }, + "fields": [ + { + "type": "address", + "id": 7, + "label": "Address", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "addressType": "international", + "inputs": [ + { + "id": "7.1", + "label": "Street Address", + "name": "", + "placeholder": "123 Main St." + }, + { "id": "7.2", "label": "Address Line 2", "name": "" }, + { "id": "7.3", "label": "City", "name": "" }, + { "id": "7.4", "label": "State / Province", "name": "" }, + { "id": "7.5", "label": "ZIP / Postal Code", "name": "" }, + { "id": "7.6", "label": "Country", "name": "" } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-address-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "defaultCountry": "", + "defaultProvince": "", + "productField": "", + "defaultState": "", + "enableCopyValuesOption": "", + "copyValuesOptionDefault": "", + "copyValuesOptionLabel": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "" + }, + { + "type": "checkbox", + "id": 8, + "label": "Which Ninja Turtles do you like?", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "choices": [ + { + "text": "Leonardo", + "value": "leonardo", + "isSelected": false, + "price": "" + }, + { + "text": "Donatello", + "value": "donatello", + "isSelected": false, + "price": "" + }, + { + "text": "Raphael", + "value": "Raphael", + "isSelected": false, + "price": "" + }, + { + "text": "Michelangelo", + "value": "michelangelo", + "isSelected": false, + "price": "" + } + ], + "inputs": [ + { "id": "8.1", "label": "Leonardo", "name": "" }, + { "id": "8.2", "label": "Donatello", "name": "" }, + { "id": "8.3", "label": "Raphael", "name": "" }, + { "id": "8.4", "label": "Michelangelo", "name": "" } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-checkboxes", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "conditionalLogic": "", + "productField": "", + "enableSelectAll": "", + "enablePrice": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "enableChoiceValue": true, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "date", + "id": 3, + "label": "Date", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "dateType": "datepicker", + "calendarIconType": "none", + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "01/01/3000", + "cssClass": "my-cool-date-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "calendarIconUrl": "", + "dateFormat": "", + "productField": "", + "fields": "", + "displayOnly": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "email", + "id": 9, + "label": "Email", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "some.cool.cat@example.com", + "cssClass": "my-cool-email-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "emailConfirmEnabled": "", + "fields": "", + "displayOnly": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "multiselect", + "id": 10, + "label": "Scenarios in which you would eat green eggs and ham", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "storageType": "json", + "inputs": null, + "choices": [ + { + "text": "In a house", + "value": "in_a_house", + "isSelected": false, + "price": "" + }, + { + "text": "With a mouse", + "value": "with_a_mouse", + "isSelected": false, + "price": "" + }, + { + "text": "In a box", + "value": "in_a_box", + "isSelected": false, + "price": "" + }, + { + "text": "With a fox", + "value": "with_a_fox", + "isSelected": false, + "price": "" + }, + { + "text": "In the rain", + "value": "in_the_rain", + "isSelected": false, + "price": "" + }, + { + "text": "On a train", + "value": "on_a_train", + "isSelected": false, + "price": "" + }, + { + "text": "In a boat", + "value": "in_a_boat", + "isSelected": false, + "price": "" + }, + { + "text": "With a goat", + "value": "with_a_goat", + "isSelected": false, + "price": "" + } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-multiselect", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "conditionalLogic": "", + "enableEnhancedUI": false, + "productField": "", + "multiSelectSize": "", + "enablePrice": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "enableChoiceValue": true, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "name", + "id": 6, + "label": "Name", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "nameFormat": "advanced", + "inputs": [ + { + "id": "6.2", + "label": "Prefix", + "name": "", + "choices": [ + { + "text": "Mr.", + "value": "Mr.", + "isSelected": false, + "price": "" + }, + { + "text": "Mrs.", + "value": "Mrs.", + "isSelected": false, + "price": "" + }, + { + "text": "Miss", + "value": "Miss", + "isSelected": false, + "price": "" + }, + { + "text": "Ms.", + "value": "Ms.", + "isSelected": false, + "price": "" + }, + { + "text": "Dr.", + "value": "Dr.", + "isSelected": false, + "price": "" + }, + { + "text": "Prof.", + "value": "Prof.", + "isSelected": false, + "price": "" + }, + { + "text": "Rev.", + "value": "Rev.", + "isSelected": false, + "price": "" + } + ], + "isHidden": false, + "inputType": "radio" + }, + { "id": "6.3", "label": "First", "name": "" }, + { + "id": "6.4", + "label": "Middle", + "name": "", + "isHidden": false, + "placeholder": "The name your parents include when you're in trouble" + }, + { "id": "6.6", "label": "Last", "name": "" }, + { "id": "6.8", "label": "Suffix", "name": "", "isHidden": false } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-name-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "phone", + "id": 4, + "label": "Phone", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "phoneFormat": "standard", + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "123-456-7890", + "cssClass": "my-cool-phone-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "radio", + "id": 5, + "label": "What kind of bear is best?", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "choices": [ + { + "text": "Brown bear", + "value": "brown_bear", + "isSelected": false, + "price": "" + }, + { + "text": "Black bear", + "value": "black_bear", + "isSelected": false, + "price": "" + }, + { + "text": "Grizzly bear", + "value": "grizzly_bear", + "isSelected": false, + "price": "" + } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-radio-buttons", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "conditionalLogic": "", + "productField": "", + "enableOtherChoice": "", + "enablePrice": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "enableChoiceValue": true, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "select", + "id": 11, + "label": "Which was the first Pok\u00e9mon you ever acquired?", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "choices": [ + { + "text": "Squirtle", + "value": "squirtle", + "isSelected": false, + "price": "" + }, + { + "text": "Charmander", + "value": "charmander", + "isSelected": false, + "price": "" + }, + { + "text": "Bulbasaur", + "value": "bulbasaur", + "isSelected": false, + "price": "" + }, + { + "text": "Pikachu", + "value": "pikachu", + "isSelected": false, + "price": "" + }, + { + "text": "Other", + "value": "other", + "isSelected": false, + "price": "" + }, + { + "text": "I've never acquired one :(", + "value": "none", + "isSelected": false, + "price": "" + } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-select-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "conditionalLogic": "", + "productField": "", + "enablePrice": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "enableChoiceValue": true, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "textarea", + "id": 12, + "label": "How much wood would a woodchuck chuck if a woodchuck could chuck wood?", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-textarea-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "form_id": "", + "useRichTextEditor": false, + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "text", + "id": 13, + "label": "How dare you?", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "This better be good...", + "cssClass": "my-cool-text-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "enablePasswordInput": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "time", + "id": 2, + "label": "Time", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": [ + { "id": "2.1", "label": "HH", "name": "" }, + { "id": "2.2", "label": "MM", "name": "" }, + { "id": "2.3", "label": "AM/PM", "name": "" } + ], + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "my-cool-time-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "timeFormat": "12", + "productField": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "fields": "", + "displayOnly": "", + "enableAutocomplete": false, + "autocompleteAttribute": "", + "checkboxLabel": "" + }, + { + "type": "website", + "id": 14, + "label": "Website", + "adminLabel": "", + "isRequired": false, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "placeholder": "https://example.com", + "formId": "3", + "description": "", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": "", + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "cssClass": "my-cool-website-field", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "fields": "", + "displayOnly": "", + "autocompleteAttribute": "", + "enableAutocomplete": false, + "checkboxLabel": "" + }, + { + "type": "text", + "id": 15, + "label": "Short Strings Only", + "adminLabel": "", + "isRequired": true, + "size": "medium", + "errorMessage": "", + "visibility": "visible", + "inputs": null, + "formId": "3", + "description": "Please enter 5 characters or less.", + "allowsPrepopulate": false, + "inputMask": false, + "inputMaskValue": "", + "inputMaskIsCustom": false, + "maxLength": 5, + "inputType": "", + "labelPlacement": "", + "descriptionPlacement": "", + "subLabelPlacement": "", + "placeholder": "", + "cssClass": "", + "inputName": "", + "noDuplicates": false, + "defaultValue": "", + "choices": "", + "conditionalLogic": "", + "productField": "", + "enablePasswordInput": "", + "fields": "", + "displayOnly": "", + "multipleFiles": false, + "maxFiles": "", + "calculationFormula": "", + "calculationRounding": "", + "enableCalculation": "", + "disableQuantity": false, + "displayAllCategories": false, + "useRichTextEditor": false, + "layoutGroupId": "79e7fd63", + "autocompleteAttribute": "", + "enableAutocomplete": false, + "errors": [], + "layoutGridColumnSpan": 12, + "checkboxLabel": "" + } + ], + "version": "2.5.0.1", + "id": "3", + "nextFieldId": 16, + "useCurrentUserAsAuthor": true, + "postContentTemplateEnabled": false, + "postTitleTemplateEnabled": false, + "postTitleTemplate": "", + "postContentTemplate": "", + "lastPageButton": null, + "pagination": null, + "firstPageCssClass": null, + "is_active": "1", + "date_created": "2021-04-29 11:24:48", + "is_trash": "0", + "subLabelPlacement": "below", + "cssClass": "", + "enableHoneypot": false, + "enableAnimation": false, + "save": { + "enabled": false, + "button": { "type": "link", "text": "Save and Continue Later" } + }, + "limitEntries": false, + "limitEntriesCount": "", + "limitEntriesPeriod": "", + "limitEntriesMessage": "", + "scheduleForm": false, + "scheduleStart": "", + "scheduleStartHour": "", + "scheduleStartMinute": "", + "scheduleStartAmpm": "", + "scheduleEnd": "", + "scheduleEndHour": "", + "scheduleEndMinute": "", + "scheduleEndAmpm": "", + "schedulePendingMessage": "", + "scheduleMessage": "", + "requireLogin": false, + "requireLoginMessage": "", + "confirmations": [ + { + "id": "608a97805a96d", + "name": "Default Confirmation", + "isDefault": true, + "type": "message", + "message": "Thanks for contacting us! We will get in touch with you shortly.", + "url": "", + "pageId": "", + "queryString": "" + } + ], + "notifications": [ + { + "id": "608a97805a5b1", + "isActive": true, + "to": "{admin_email}", + "name": "Admin Notification", + "event": "form_submission", + "toType": "email", + "subject": "New submission from {form_title}", + "message": "{all_fields}" + } + ] + }, + "version": "2.5.0.1" +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/layouts/default.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/layouts/default.vue new file mode 100644 index 0000000..d300a60 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/layouts/default.vue @@ -0,0 +1,25 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/layouts/products.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/layouts/products.vue new file mode 100644 index 0000000..464f24e --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/layouts/products.vue @@ -0,0 +1,25 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/nuxt.config.ts b/examples/nuxt/nuxt-headlesswp-gravity-forms/nuxt.config.ts new file mode 100644 index 0000000..e926da8 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/nuxt.config.ts @@ -0,0 +1,19 @@ +// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + compatibilityDate: "2024-11-01", + devtools: { enabled: true }, + modules: ["@nuxtjs/tailwindcss"], + runtimeConfig: { + public: { + wordpressUrl: "", + }, + }, + routeRules: { + // Blog listing page - revalidates every 60 seconds + "/wpblog": { isr: 60 }, + // Individual blog posts - cached until next deployment + "/wpblog/**": { isr: true }, + // Pre-render the form page at build time + "/headlesswp-gform": { prerender: true }, + }, +}); diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/package.json b/examples/nuxt/nuxt-headlesswp-gravity-forms/package.json new file mode 100644 index 0000000..652f48e --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/package.json @@ -0,0 +1,25 @@ +{ + "name": "nuxt-app", + "private": true, + "type": "module", + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview", + "postinstall": "nuxt prepare", + "start": "nuxt start" + }, + "dependencies": { + "graphql": "^16.10.0", + "nuxt": "^3.16.0", + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@nuxtjs/tailwindcss": "^6.13.2" + }, + "engines": { + "node": ">=18.19.0" + } +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/index.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/index.vue new file mode 100644 index 0000000..2178436 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/index.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/products/[id].vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/products/[id].vue new file mode 100644 index 0000000..20db8d9 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/products/[id].vue @@ -0,0 +1,17 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/products/index.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/products/index.vue new file mode 100644 index 0000000..b571091 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/products/index.vue @@ -0,0 +1,20 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/questionnaire/index.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/questionnaire/index.vue new file mode 100644 index 0000000..98a510a --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/questionnaire/index.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/wpblog/[...uri].vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/wpblog/[...uri].vue new file mode 100644 index 0000000..16b3d4d --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/wpblog/[...uri].vue @@ -0,0 +1,48 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/wpblog/index.vue b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/wpblog/index.vue new file mode 100644 index 0000000..bf0ee4e --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/pages/wpblog/index.vue @@ -0,0 +1,42 @@ + + + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/public/favicon.ico b/examples/nuxt/nuxt-headlesswp-gravity-forms/public/favicon.ico new file mode 100644 index 0000000..18993ad Binary files /dev/null and b/examples/nuxt/nuxt-headlesswp-gravity-forms/public/favicon.ico differ diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/public/robots.txt b/examples/nuxt/nuxt-headlesswp-gravity-forms/public/robots.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/public/robots.txt @@ -0,0 +1 @@ + diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/server/tsconfig.json b/examples/nuxt/nuxt-headlesswp-gravity-forms/server/tsconfig.json new file mode 100644 index 0000000..b9ed69c --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/server/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../.nuxt/tsconfig.server.json" +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/tsconfig.json b/examples/nuxt/nuxt-headlesswp-gravity-forms/tsconfig.json new file mode 100644 index 0000000..a746f2a --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/tsconfig.json @@ -0,0 +1,4 @@ +{ + // https://nuxt.com/docs/guide/concepts/typescript + "extends": "./.nuxt/tsconfig.json" +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/.wp-env.json b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/.wp-env.json new file mode 100644 index 0000000..810c6ed --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/.wp-env.json @@ -0,0 +1,23 @@ +{ + "phpVersion": "7.4", + "plugins": [ + "https://github.com/wp-graphql/wp-graphql/releases/latest/download/wp-graphql.zip", + "https://github.com/AxeWP/wp-graphql-gravity-forms/releases/latest/download/wp-graphql-gravity-forms.zip" + ], + "config": { + "WP_DEBUG": true, + "SCRIPT_DEBUG": false, + "GRAPHQL_DEBUG": true, + "WP_DEBUG_LOG": true, + "WP_DEBUG_DISPLAY": false, + "SAVEQUERIES": false + }, + "mappings": { + "db": "./wp-env/db", + "wp-content/uploads": "./wp-env/uploads", + ".htaccess": "./wp-env/setup/.htaccess" + }, + "lifecycleScripts": { + "afterStart": "wp-env run cli -- wp rewrite structure '/%postname%/' && wp-env run cli -- wp rewrite flush" + } +} diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/README.md b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/README.md new file mode 100644 index 0000000..4539d75 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/README.md @@ -0,0 +1,106 @@ +# Example: Gravity Forms in Headless WordPress with Nuxt/Vue + +This example shows you how to wire up a full headless WordPress backend—complete with Gravity Forms, WPGraphQL, and a pre-built "Questionnaire" form—alongside a Nuxt 3 front end that dynamically renders your forms using the GraphQL interfaces feature of WPGraphQL for Gravity Forms. + +## Features + +- **Headless WordPress with Gravity Forms** + + - Stand up WP + WPGraphQL + Gravity Forms (and your Questionnaire JSON) in one command. + +- **Dynamic form rendering** + + - Use GraphQL interfaces (`GfFieldWithLabelSetting`, `GfFieldWithChoicesSetting`, etc.) plus a single `useFormFields` composable to automatically map any new field type to the correct Vue component. + +- **Composable Nuxt 3 front end** + + - Fetch your form schema with `useGravityForm` and render all fields via a dynamic `` pattern. + +- **Out-of-the-box data** + - Includes a SQL dump of WP + Gravity Forms data and a zipped uploads folder so you can dive right in. + +## Project Structure + +``` +├── components/ # Vue form-field components & barrel file +├── composables/ # useGravityForm.js & useFormFields.js +├── pages/ # Nuxt page (index.vue) that renders the form +├── wp-env/ +│ ├── db/ +│ │ └── database.sql # WordPress + Gravity Forms schema & data +│ ├── setup/ +│ │ └── .htaccess # CORS + pretty-permalinks for wp-env +│ └── uploads.zip # wp-content/uploads media files +├── .wp-env.json # @wordpress/env configuration +├── package.json # wp-env + Nuxt dev scripts +└── nuxt.config.ts # Nuxt application config +``` + +## Running the Example with wp-env + +### Prerequisites + +- Node.js (v18+ recommended) +- npm +- Docker (so that @wordpress/env can spin up the WP container) + +### Setup Repository and Install + +```bash +git clone https://github.com/your-org/nuxt-gravityforms-example.git +cd nuxt-gravityforms-example +pnpm install + +echo "NUXT_PUBLIC_WORDPRESS_URL=http://localhost:8888" > .env +``` + +### Quick Start + +1. Unzip uploads, start WP, import DB, then launch Nuxt: + + ```bash + pnpm example:build + ``` + +2. Or run steps separately: + + ```bash + # Start WP + pnpm wp:start + + # Import DB + pnpm wp:db:import + + # Unzip uploads + pnpm wp:images:unzip + + # Start Nuxt dev server + pnpm dev + ``` + +By the end, you will have: + +- WordPress Admin: http://localhost:8888/wp-admin/ + - user: admin / pass: password +- Nuxt Front End: http://localhost:3000/ + +## Scripts + +| Command | Description | +| ---------------------- | ---------------------------------------------------------------- | +| `pnpm example:build` | Unzip media → start WP env → import DB → launch Nuxt dev server | +| `pnpm example:start` | Start WP env, then Nuxt dev server | +| `pnpm example:stop` | Stop the WordPress environment (wp-env stop) | +| `pnpm example:prune` | Destroy & rebuild the WP environment, then restart—all in one go | +| `pnpm wp:start` | @wordpress/env start (launches PHP/MySQL container with WP) | +| `pnpm wp:stop` | @wordpress/env stop | +| `pnpm wp:db:import` | Import the SQL dump into the running WP container | +| `pnpm wp:db:export` | Export the current WP database back to wp-env/db/database.sql | +| `pnpm wp:images:unzip` | Clear & unzip wp-env/uploads.zip → populates wp-content/uploads | +| `pnpm dev` | Start the Nuxt 3 development server on port 3000 | + +> **Tip:** You can also run any arbitrary WP-CLI command inside the container via `pnpm wp:cli -- ` + +## Database Access + +If you need direct database access (phpMyAdmin), add a `"phpmyadminPort": 11111` entry to your `.wp-env.json` and then navigate to http://localhost:11111. diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/package.json b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/package.json new file mode 100644 index 0000000..6c2b6f7 --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/package.json @@ -0,0 +1,18 @@ +{ + "name": "headlesswp-gravity-forms-nuxt-env", + "version": "1.0.0", + "scripts": { + "wp:start": "wp-env start", + "wp:stop": "wp-env stop", + "wp:destroy": "wp-env destroy --config .wp-env.json", + "wp:db:import": "wp-env run cli -- wp db import /var/www/html/db/database.sql", + "wp:db:export": "wp-env run cli -- wp db export /var/www/html/db/database.sql", + "wp:images:unzip": "rm -rf ./wp-content/uploads && unzip uploads.zip -d wp-content/uploads", + "frontend:dev": "pnpm --prefix .. dev", + "start": "npm run wp:start && npm run frontend:dev" + }, + "devDependencies": { + "@wordpress/env": "^10.20.0" + } + } + \ No newline at end of file diff --git a/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/setup/.htaccess b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/setup/.htaccess new file mode 100644 index 0000000..ad8663f --- /dev/null +++ b/examples/nuxt/nuxt-headlesswp-gravity-forms/wp-env/setup/.htaccess @@ -0,0 +1,21 @@ + + Header set Access-Control-Allow-Origin "*" + Header set Access-Control-Allow-Methods "GET, POST, OPTIONS" + Header set Access-Control-Allow-Headers "Authorization, Content-Type" + + +# BEGIN WordPress +# The directives (lines) between "BEGIN WordPress" and "END WordPress" are +# dynamically generated, and should only be modified via WordPress filters. +# Any changes to the directives between these markers will be overwritten. + +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteBase / +RewriteRule ^index\.php$ - [L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] + + +# END WordPress \ No newline at end of file