# Integration Guide

Complete guide for integrating Form Builder into your application.

## CDN Integration

### Basic Iframe Embedding

```html
<iframe
  src="https://picazru.github.io/form-builder/dist/index.html"
  width="100%"
  height="600px"
  frameborder="0"
></iframe>
```

### With Custom Schema

```html
<iframe
  src="https://picazru.github.io/form-builder/dist/index.html?schema=BASE64_SCHEMA"
  width="100%"
  height="600px"
></iframe>
```

**Encoding Schema:**

```javascript
const schema = { version: "0.3", title: "My Form", elements: [...] };
const encodedSchema = btoa(JSON.stringify(schema));
const url = `https://picazru.github.io/form-builder/dist/index.html?schema=${encodedSchema}`;
```

## NPM Integration

### Installation

```bash
npm install @dmitryvim/form-builder
```

### Direct HTML Integration

Copy the form builder HTML directly into your application:

```javascript
import formBuilderHTML from "@dmitryvim/form-builder/dist/index.html";

// Insert into your page
document.getElementById("form-container").innerHTML = formBuilderHTML;
```

## Configuration

### File Upload Handler

The upload handler **must return a resource ID** that can be used for downloads and form submission:

```javascript
window.addEventListener("message", (event) => {
  if (event.origin !== "https://picazru.github.io") return;

  if (event.data.type === "formBuilderReady") {
    const iframe = document.getElementById("formBuilder");
    iframe.contentWindow.postMessage(
      {
        type: "configure",
        config: {
          uploadHandler: async (file) => {
            const formData = new FormData();
            formData.append("file", file);

            const response = await fetch("/api/upload", {
              method: "POST",
              body: formData,
            });

            const result = await response.json();
            return result.resourceId; // Return ID, not URL
          },
          downloadHandler: async (resourceId) => {
            window.open(`/api/download/${resourceId}`, "_blank");
          },
          thumbnailHandler: async (resourceId) => {
            return `/api/thumbnail/${resourceId}`;
          },
        },
      },
      "https://picazru.github.io",
    );
  }
});
```

### Read-Only Mode

Display form data without editing capabilities:

```javascript
iframe.contentWindow.postMessage(
  {
    type: "configure",
    options: {
      readonly: true,
    },
  },
  "https://picazru.github.io",
);
```

### Tailwind CSS Integration

Always uses Tailwind CSS styling. Add to your CSS:

```css
/* Custom properties for consistent styling */
:root {
  --form-primary: 59 130 246; /* Blue */
  --form-secondary: 100 116 139; /* Gray */
  --form-accent: 16 185 129; /* Green */
}

/* Override form builder styles if needed */
.form-builder-container {
  @apply max-w-none; /* Remove max-width constraints */
}
```

## Events

### Form Submission

Listen for form submissions:

```javascript
window.addEventListener("message", (event) => {
  if (event.data.type === "formSubmit") {
    const { data, schema } = event.data;
    console.log("Form submitted:", data);

    // Handle the submission
    handleFormSubmission(data);
  }
});
```

### Draft Saving

Listen for draft save events:

```javascript
window.addEventListener("message", (event) => {
  if (event.data.type === "draftSave") {
    const { data, schema } = event.data;
    console.log("Draft saved:", data);

    // Save draft without validation
    saveDraft(data);
  }
});
```

## API Communication

### Send Schema

```javascript
iframe.contentWindow.postMessage({
    type: 'setSchema',
    schema: {
        version: "0.3",
        title: "Contact Form",
        elements: [...]
    }
}, 'https://picazru.github.io');
```

### Prefill Form Data

```javascript
iframe.contentWindow.postMessage(
  {
    type: "setData",
    data: {
      name: "John Doe",
      email: "john@example.com",
    },
  },
  "https://picazru.github.io",
);
```

### Get Current Data

```javascript
// Request data
iframe.contentWindow.postMessage(
  {
    type: "getData",
  },
  "https://picazru.github.io",
);

// Listen for response
window.addEventListener("message", (event) => {
  if (event.data.type === "currentData") {
    console.log("Current form data:", event.data.data);
  }
});
```

### Validate Form

```javascript
// Request validation
iframe.contentWindow.postMessage(
  {
    type: "validate",
  },
  "https://picazru.github.io",
);

// Listen for result
window.addEventListener("message", (event) => {
  if (event.data.type === "validationResult") {
    console.log("Form is valid:", event.data.isValid);
  }
});
```

## Backend Integration

### File Upload Endpoint

Your backend must handle file uploads and return resource IDs:

```javascript
// Express.js example
app.post("/api/upload", upload.single("file"), (req, res) => {
  const file = req.file;

  // Store file (S3, local storage, etc.)
  const resourceId = generateResourceId();
  storeFile(resourceId, file);

  res.json({
    resourceId: resourceId,
    filename: file.originalname,
    size: file.size,
    mimetype: file.mimetype,
  });
});
```

### File Download Endpoint

```javascript
app.get("/api/download/:resourceId", (req, res) => {
  const { resourceId } = req.params;

  // Retrieve file info
  const fileInfo = getFileInfo(resourceId);
  if (!fileInfo) {
    return res.status(404).json({ error: "File not found" });
  }

  // Stream file to client
  const fileStream = getFileStream(resourceId);
  res.setHeader(
    "Content-Disposition",
    `attachment; filename="${fileInfo.filename}"`,
  );
  res.setHeader("Content-Type", fileInfo.mimetype);
  fileStream.pipe(res);
});
```

### Form Submission Handler

```javascript
app.post("/api/forms/submit", (req, res) => {
  const { schema, data } = req.body;

  // Process form data
  // File fields will contain resource IDs

  console.log("Received form:", data);
  // Example: { name: "John", avatar: "res_abc123", documents: ["res_def456", "res_ghi789"] }

  // Validate and save
  const result = processFormSubmission(schema, data);

  res.json({ success: true, id: result.id });
});
```

## Framework-Specific Examples

### React Integration

```jsx
import { useEffect, useRef } from "react";

function FormBuilder({ schema, onSubmit }) {
  const iframeRef = useRef(null);

  useEffect(() => {
    const handleMessage = (event) => {
      if (event.data.type === "formBuilderReady") {
        // Configure form builder
        iframeRef.current.contentWindow.postMessage(
          {
            type: "setSchema",
            schema: schema,
          },
          "https://picazru.github.io",
        );
      }

      if (event.data.type === "formSubmit") {
        onSubmit(event.data.data);
      }
    };

    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, [schema, onSubmit]);

  return (
    <iframe
      ref={iframeRef}
      src="https://picazru.github.io/form-builder/dist/index.html"
      width="100%"
      height="600px"
      frameBorder="0"
    />
  );
}
```

### Vue.js Integration

```vue
<template>
  <iframe
    ref="formBuilder"
    src="https://picazru.github.io/form-builder/dist/index.html"
    width="100%"
    height="600px"
    frameborder="0"
    @load="onIframeLoad"
  />
</template>

<script>
export default {
  props: ["schema"],

  mounted() {
    window.addEventListener("message", this.handleMessage);
  },

  beforeUnmount() {
    window.removeEventListener("message", this.handleMessage);
  },

  methods: {
    handleMessage(event) {
      if (event.data.type === "formBuilderReady") {
        this.$refs.formBuilder.contentWindow.postMessage(
          {
            type: "setSchema",
            schema: this.schema,
          },
          "https://picazru.github.io",
        );
      }

      if (event.data.type === "formSubmit") {
        this.$emit("submit", event.data.data);
      }
    },
  },
};
</script>
```

## Security Considerations

### Origin Validation

Always validate message origins:

```javascript
window.addEventListener("message", (event) => {
  // Only accept messages from form builder origin
  if (event.origin !== "https://picazru.github.io") {
    return;
  }

  // Process message...
});
```

### File Upload Security

- **Validate file types** on server
- **Limit file sizes** appropriately
- **Scan for viruses** if possible
- **Use secure file storage** (S3 with proper permissions)
- **Generate secure resource IDs** (UUID, crypto.randomUUID())

### Content Security Policy

Add to your CSP headers:

```
Content-Security-Policy:
    default-src 'self';
    frame-src https://picazru.github.io;
    connect-src 'self' https://your-api.com;
```

## Troubleshooting

### Common Issues

**Form builder not loading**: Check CSP settings and iframe src URL

**File uploads failing**: Verify upload handler returns resource ID, not URL

**Messages not working**: Confirm origin validation and message format

**Schema not applying**: Check schema format against [Schema Reference](schema.md)

### Debugging

Enable console logging in iframe:

```javascript
iframe.contentWindow.postMessage(
  {
    type: "configure",
    config: {
      debug: true,
    },
  },
  "https://picazru.github.io",
);
```

This will log all form builder events to the browser console.

## Migration

### From v0.1.x to v0.2.x

- Upload handlers must return resource IDs instead of URLs
- Added support for draft saving
- Improved Tailwind CSS integration

### Updating Handlers

```javascript
// Old (v0.1.x)
uploadHandler: async (file) => {
  // ... upload logic
  return result.fileUrl; // URL
};

// New (v0.2.x)
uploadHandler: async (file) => {
  // ... upload logic
  return result.resourceId; // ID
};
```

This integration guide covers all aspects of using Form Builder in production applications.
