- Introduced iframe mode detection to streamline the user experience when embedded in other applications. - Added functionality to update the URL upon process completion, allowing for better integration with parent applications. - Implemented error notification to the parent iframe, ensuring that any issues during workflow execution are communicated effectively. - Enhanced UI responsiveness by adjusting styles based on iframe mode, improving overall usability and visual consistency. - Updated process completion and error handling logic to support seamless multi-process workflows.
725 lines
21 KiB
Markdown
725 lines
21 KiB
Markdown
# Iframe Integration Guide
|
|
|
|
This guide explains how to integrate Corrad workflow forms into external applications using iframes. This enables seamless embedding of business processes into customer portals, external websites, or any web application.
|
|
|
|
## Table of Contents
|
|
|
|
- [Overview](#overview)
|
|
- [Basic Integration](#basic-integration)
|
|
- [Advanced Configuration](#advanced-configuration)
|
|
- [Multi-Process Workflows](#multi-process-workflows)
|
|
- [Error Handling](#error-handling)
|
|
- [Security Considerations](#security-considerations)
|
|
- [Examples](#examples)
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
## Overview
|
|
|
|
Corrad provides a powerful iframe integration system that allows you to embed workflow forms into external applications. The system supports:
|
|
|
|
- **Clean UI Mode**: Hide all debug information and UI chrome
|
|
- **Seamless Multi-Process**: Chain multiple workflows together
|
|
- **Real-time Communication**: Parent-child message passing
|
|
- **Error Handling**: Graceful error management
|
|
- **Progress Tracking**: Monitor workflow completion
|
|
|
|
## Basic Integration
|
|
|
|
### Simple Iframe Embedding
|
|
|
|
```html
|
|
<!-- Basic iframe integration -->
|
|
<iframe
|
|
src="/workflow/your-process-id?debug=false"
|
|
width="100%"
|
|
height="600px"
|
|
style="border: none;">
|
|
</iframe>
|
|
```
|
|
|
|
### URL Parameters
|
|
|
|
| Parameter | Description | Example |
|
|
|-----------|-------------|---------|
|
|
| `debug=false` | Enable iframe mode (hide UI chrome) | `?debug=false` |
|
|
| `hideComplete=true` | Hide completion message | `?hideComplete=true` |
|
|
| `theme=dark` | Apply custom theme (if supported) | `?theme=dark` |
|
|
|
|
### Complete Example
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Workflow Integration</title>
|
|
<style>
|
|
.workflow-container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
.workflow-iframe {
|
|
width: 100%;
|
|
height: 600px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="workflow-container">
|
|
<h1>Complete Your Application</h1>
|
|
<iframe
|
|
id="workflow-iframe"
|
|
src="/workflow/application-form?debug=false&hideComplete=true"
|
|
class="workflow-iframe">
|
|
</iframe>
|
|
</div>
|
|
|
|
<script>
|
|
// Listen for workflow completion
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data.type === 'workflow-complete') {
|
|
console.log('Workflow completed:', event.data);
|
|
// Handle completion
|
|
showSuccessMessage('Application submitted successfully!');
|
|
} else if (event.data.type === 'workflow-error') {
|
|
console.error('Workflow error:', event.data);
|
|
showErrorMessage(event.data.error);
|
|
}
|
|
});
|
|
|
|
function showSuccessMessage(message) {
|
|
alert(message);
|
|
}
|
|
|
|
function showErrorMessage(error) {
|
|
alert('Error: ' + error);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
## Advanced Configuration
|
|
|
|
### Message Event Structure
|
|
|
|
The workflow sends the following message types to the parent iframe:
|
|
|
|
#### Completion Message
|
|
```javascript
|
|
{
|
|
type: 'workflow-complete',
|
|
processId: 'application-form',
|
|
processName: 'Application Form',
|
|
hideCompletionMessage: true,
|
|
timestamp: '2024-01-15T10:30:00.000Z'
|
|
}
|
|
```
|
|
|
|
#### Error Message
|
|
```javascript
|
|
{
|
|
type: 'workflow-error',
|
|
processId: 'application-form',
|
|
processName: 'Application Form',
|
|
error: 'API call failed: Network error',
|
|
timestamp: '2024-01-15T10:30:00.000Z'
|
|
}
|
|
```
|
|
|
|
### Advanced Integration Example
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Advanced Workflow Integration</title>
|
|
<style>
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
.header {
|
|
background: #f8f9fa;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 8px;
|
|
background: #e9ecef;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: #007bff;
|
|
transition: width 0.3s ease;
|
|
}
|
|
.workflow-iframe {
|
|
width: 100%;
|
|
height: 600px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
}
|
|
.status {
|
|
margin-top: 10px;
|
|
font-size: 14px;
|
|
color: #6c757d;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Employee Onboarding</h1>
|
|
<div class="progress-bar">
|
|
<div id="progress-fill" class="progress-fill" style="width: 0%"></div>
|
|
</div>
|
|
<div id="status" class="status">Starting process...</div>
|
|
</div>
|
|
|
|
<iframe
|
|
id="workflow-iframe"
|
|
src="/workflow/personal-info?debug=false&hideComplete=true"
|
|
class="workflow-iframe">
|
|
</iframe>
|
|
</div>
|
|
|
|
<script>
|
|
let currentStep = 0;
|
|
const totalSteps = 5;
|
|
|
|
// Workflow steps configuration
|
|
const workflowSteps = [
|
|
{ id: 'personal-info', name: 'Personal Information' },
|
|
{ id: 'employment', name: 'Employment Details' },
|
|
{ id: 'documents', name: 'Document Upload' },
|
|
{ id: 'benefits', name: 'Benefits Selection' },
|
|
{ id: 'final', name: 'Final Review' }
|
|
];
|
|
|
|
// Listen for workflow messages
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data.type === 'workflow-complete') {
|
|
console.log('Workflow step completed:', event.data);
|
|
|
|
if (event.data.hideCompletionMessage) {
|
|
// Auto-advance to next step
|
|
setTimeout(() => {
|
|
loadNextStep();
|
|
}, 100);
|
|
}
|
|
} else if (event.data.type === 'workflow-error') {
|
|
console.error('Workflow error:', event.data);
|
|
showError(event.data.error);
|
|
}
|
|
});
|
|
|
|
function loadNextStep() {
|
|
currentStep++;
|
|
|
|
if (currentStep >= workflowSteps.length) {
|
|
// All steps complete
|
|
showFinalSuccess();
|
|
return;
|
|
}
|
|
|
|
const step = workflowSteps[currentStep];
|
|
const iframe = document.getElementById('workflow-iframe');
|
|
|
|
// Update iframe source
|
|
iframe.src = `/workflow/${step.id}?debug=false&hideComplete=true`;
|
|
|
|
// Update progress
|
|
updateProgress();
|
|
}
|
|
|
|
function updateProgress() {
|
|
const progress = ((currentStep + 1) / totalSteps) * 100;
|
|
document.getElementById('progress-fill').style.width = progress + '%';
|
|
document.getElementById('status').textContent =
|
|
`Step ${currentStep + 1} of ${totalSteps}: ${workflowSteps[currentStep].name}`;
|
|
}
|
|
|
|
function showFinalSuccess() {
|
|
document.getElementById('workflow-iframe').style.display = 'none';
|
|
document.querySelector('.header').innerHTML = `
|
|
<h1>🎉 Onboarding Complete!</h1>
|
|
<p>All required information has been submitted successfully.</p>
|
|
<button onclick="downloadReport()">Download Report</button>
|
|
`;
|
|
}
|
|
|
|
function showError(error) {
|
|
document.getElementById('status').textContent = `Error: ${error}`;
|
|
document.getElementById('status').style.color = '#dc3545';
|
|
}
|
|
|
|
function downloadReport() {
|
|
// Implement report download logic
|
|
alert('Report download functionality would be implemented here');
|
|
}
|
|
|
|
// Initialize progress
|
|
updateProgress();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
## Multi-Process Workflows
|
|
|
|
### Seamless Multi-Step Process
|
|
|
|
For complex workflows with multiple steps, you can chain processes together seamlessly:
|
|
|
|
```javascript
|
|
// Multi-process workflow configuration
|
|
const processes = [
|
|
{ id: 'personal-info', name: 'Personal Information' },
|
|
{ id: 'employment', name: 'Employment Details' },
|
|
{ id: 'documents', name: 'Document Upload' },
|
|
{ id: 'references', name: 'References' },
|
|
{ id: 'background', name: 'Background Check' },
|
|
{ id: 'medical', name: 'Medical Information' },
|
|
{ id: 'emergency', name: 'Emergency Contacts' },
|
|
{ id: 'benefits', name: 'Benefits Selection' },
|
|
{ id: 'payroll', name: 'Payroll Information' },
|
|
{ id: 'final', name: 'Final Review' }
|
|
];
|
|
|
|
let currentProcessIndex = 0;
|
|
|
|
function loadProcess(processIndex) {
|
|
if (processIndex >= processes.length) {
|
|
// All processes complete
|
|
showFinalSuccess();
|
|
return;
|
|
}
|
|
|
|
const process = processes[processIndex];
|
|
const iframe = document.getElementById('workflow-iframe');
|
|
|
|
// Load process with hidden completion message
|
|
iframe.src = `/workflow/${process.id}?debug=false&hideComplete=true`;
|
|
currentProcessIndex = processIndex;
|
|
|
|
// Update progress indicator
|
|
updateProgress(processIndex + 1, processes.length);
|
|
}
|
|
|
|
// Listen for completion and auto-advance
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data.type === 'workflow-complete') {
|
|
console.log(`Process ${event.data.processName} completed`);
|
|
|
|
if (event.data.hideCompletionMessage) {
|
|
// Seamlessly move to next process
|
|
setTimeout(() => {
|
|
loadProcess(currentProcessIndex + 1);
|
|
}, 100);
|
|
}
|
|
} else if (event.data.type === 'workflow-error') {
|
|
console.error('Process failed:', event.data.error);
|
|
showErrorMessage(event.data.error);
|
|
}
|
|
});
|
|
|
|
// Start the first process
|
|
loadProcess(0);
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Comprehensive Error Management
|
|
|
|
```javascript
|
|
// Enhanced error handling
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data.type === 'workflow-complete') {
|
|
handleWorkflowComplete(event.data);
|
|
} else if (event.data.type === 'workflow-error') {
|
|
handleWorkflowError(event.data);
|
|
}
|
|
});
|
|
|
|
function handleWorkflowComplete(data) {
|
|
console.log('Workflow completed:', data);
|
|
|
|
// Store completion data
|
|
localStorage.setItem('workflow-completion', JSON.stringify(data));
|
|
|
|
// Update UI
|
|
updateCompletionUI(data);
|
|
|
|
// Auto-advance if configured
|
|
if (data.hideCompletionMessage) {
|
|
setTimeout(() => {
|
|
loadNextProcess();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
function handleWorkflowError(data) {
|
|
console.error('Workflow error:', data);
|
|
|
|
// Log error for debugging
|
|
console.error('Error details:', {
|
|
processId: data.processId,
|
|
processName: data.processName,
|
|
error: data.error,
|
|
timestamp: data.timestamp
|
|
});
|
|
|
|
// Show user-friendly error message
|
|
showErrorMessage(data.error);
|
|
|
|
// Optionally retry or allow manual restart
|
|
showRetryOptions();
|
|
}
|
|
|
|
function showErrorMessage(error) {
|
|
// Create error notification
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.className = 'error-notification';
|
|
errorDiv.innerHTML = `
|
|
<div class="error-content">
|
|
<h3>⚠️ An error occurred</h3>
|
|
<p>${error}</p>
|
|
<button onclick="retryCurrentProcess()">Retry</button>
|
|
<button onclick="skipCurrentProcess()">Skip</button>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(errorDiv);
|
|
}
|
|
|
|
function retryCurrentProcess() {
|
|
const iframe = document.getElementById('workflow-iframe');
|
|
iframe.src = iframe.src; // Reload current process
|
|
}
|
|
|
|
function skipCurrentProcess() {
|
|
// Move to next process
|
|
loadNextProcess();
|
|
}
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### Cross-Origin Communication
|
|
|
|
When integrating iframes, consider these security aspects:
|
|
|
|
1. **Origin Validation**: Verify message origin
|
|
2. **Content Security Policy**: Configure appropriate CSP headers
|
|
3. **Sandbox Attributes**: Use iframe sandbox for additional security
|
|
|
|
```html
|
|
<!-- Secure iframe configuration -->
|
|
<iframe
|
|
src="/workflow/process-id?debug=false"
|
|
sandbox="allow-scripts allow-same-origin allow-forms"
|
|
referrerpolicy="no-referrer">
|
|
</iframe>
|
|
```
|
|
|
|
### Message Validation
|
|
|
|
```javascript
|
|
// Validate message origin
|
|
window.addEventListener('message', (event) => {
|
|
// Verify origin (replace with your domain)
|
|
if (event.origin !== 'https://your-workflow-domain.com') {
|
|
console.warn('Message from unauthorized origin:', event.origin);
|
|
return;
|
|
}
|
|
|
|
// Validate message structure
|
|
if (!event.data || typeof event.data.type !== 'string') {
|
|
console.warn('Invalid message structure:', event.data);
|
|
return;
|
|
}
|
|
|
|
// Process valid message
|
|
handleWorkflowMessage(event.data);
|
|
});
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Customer Portal Integration
|
|
|
|
```html
|
|
<!-- Customer portal with workflow integration -->
|
|
<div class="customer-portal">
|
|
<header>
|
|
<h1>Welcome, {{ customer.name }}</h1>
|
|
<nav>
|
|
<a href="#dashboard">Dashboard</a>
|
|
<a href="#applications">Applications</a>
|
|
<a href="#documents">Documents</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
<div class="application-section">
|
|
<h2>Complete Your Application</h2>
|
|
<div class="progress-indicator">
|
|
<span id="progress-text">Step 1 of 5</span>
|
|
<div class="progress-bar">
|
|
<div id="progress-fill"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<iframe
|
|
id="application-iframe"
|
|
src="/workflow/customer-application?debug=false&hideComplete=true"
|
|
style="width: 100%; height: 600px; border: none;">
|
|
</iframe>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script>
|
|
// Customer portal integration
|
|
const applicationSteps = [
|
|
'personal-info',
|
|
'employment-history',
|
|
'document-upload',
|
|
'references',
|
|
'final-submission'
|
|
];
|
|
|
|
let currentStep = 0;
|
|
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data.type === 'workflow-complete') {
|
|
currentStep++;
|
|
updateProgress();
|
|
|
|
if (currentStep < applicationSteps.length) {
|
|
// Load next step
|
|
const iframe = document.getElementById('application-iframe');
|
|
iframe.src = `/workflow/${applicationSteps[currentStep]}?debug=false&hideComplete=true`;
|
|
} else {
|
|
// Application complete
|
|
showApplicationComplete();
|
|
}
|
|
}
|
|
});
|
|
|
|
function updateProgress() {
|
|
const progress = ((currentStep + 1) / applicationSteps.length) * 100;
|
|
document.getElementById('progress-fill').style.width = progress + '%';
|
|
document.getElementById('progress-text').textContent = `Step ${currentStep + 1} of ${applicationSteps.length}`;
|
|
}
|
|
|
|
function showApplicationComplete() {
|
|
document.getElementById('application-iframe').style.display = 'none';
|
|
document.querySelector('.application-section').innerHTML = `
|
|
<div class="completion-message">
|
|
<h2>🎉 Application Submitted!</h2>
|
|
<p>Your application has been successfully submitted. We'll review it and contact you soon.</p>
|
|
<button onclick="downloadApplication()">Download Application</button>
|
|
</div>
|
|
`;
|
|
}
|
|
</script>
|
|
```
|
|
|
|
### Employee Onboarding System
|
|
|
|
```html
|
|
<!-- Employee onboarding with multiple workflows -->
|
|
<div class="onboarding-system">
|
|
<div class="onboarding-header">
|
|
<h1>Employee Onboarding</h1>
|
|
<div class="step-indicator">
|
|
<div class="step active" data-step="1">Personal Info</div>
|
|
<div class="step" data-step="2">Employment</div>
|
|
<div class="step" data-step="3">Documents</div>
|
|
<div class="step" data-step="4">Benefits</div>
|
|
<div class="step" data-step="5">Final</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="workflow-container">
|
|
<iframe
|
|
id="onboarding-iframe"
|
|
src="/workflow/personal-info?debug=false&hideComplete=true"
|
|
style="width: 100%; height: 700px; border: none;">
|
|
</iframe>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Employee onboarding integration
|
|
const onboardingSteps = [
|
|
{ id: 'personal-info', name: 'Personal Information' },
|
|
{ id: 'employment', name: 'Employment Details' },
|
|
{ id: 'documents', name: 'Document Upload' },
|
|
{ id: 'benefits', name: 'Benefits Selection' },
|
|
{ id: 'final', name: 'Final Review' }
|
|
];
|
|
|
|
let currentStepIndex = 0;
|
|
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data.type === 'workflow-complete') {
|
|
console.log(`Step ${event.data.processName} completed`);
|
|
|
|
if (event.data.hideCompletionMessage) {
|
|
// Auto-advance to next step
|
|
setTimeout(() => {
|
|
loadNextStep();
|
|
}, 200);
|
|
}
|
|
} else if (event.data.type === 'workflow-error') {
|
|
handleOnboardingError(event.data);
|
|
}
|
|
});
|
|
|
|
function loadNextStep() {
|
|
currentStepIndex++;
|
|
|
|
if (currentStepIndex >= onboardingSteps.length) {
|
|
// Onboarding complete
|
|
showOnboardingComplete();
|
|
return;
|
|
}
|
|
|
|
const step = onboardingSteps[currentStepIndex];
|
|
const iframe = document.getElementById('onboarding-iframe');
|
|
|
|
// Update iframe
|
|
iframe.src = `/workflow/${step.id}?debug=false&hideComplete=true`;
|
|
|
|
// Update step indicator
|
|
updateStepIndicator(currentStepIndex + 1);
|
|
}
|
|
|
|
function updateStepIndicator(activeStep) {
|
|
document.querySelectorAll('.step').forEach((step, index) => {
|
|
if (index + 1 <= activeStep) {
|
|
step.classList.add('active');
|
|
} else {
|
|
step.classList.remove('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
function showOnboardingComplete() {
|
|
document.getElementById('onboarding-iframe').style.display = 'none';
|
|
document.querySelector('.workflow-container').innerHTML = `
|
|
<div class="completion-message">
|
|
<h2>🎉 Onboarding Complete!</h2>
|
|
<p>Welcome to the team! Your onboarding process has been completed successfully.</p>
|
|
<div class="next-steps">
|
|
<h3>Next Steps:</h3>
|
|
<ul>
|
|
<li>Check your email for login credentials</li>
|
|
<li>Complete your first-day orientation</li>
|
|
<li>Meet with your manager</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function handleOnboardingError(data) {
|
|
console.error('Onboarding error:', data);
|
|
// Show error message and retry options
|
|
showErrorModal(data.error);
|
|
}
|
|
</script>
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues and Solutions
|
|
|
|
#### 1. Iframe Not Loading
|
|
|
|
**Problem**: Iframe shows blank or doesn't load
|
|
**Solution**: Check URL parameters and CORS settings
|
|
|
|
```javascript
|
|
// Debug iframe loading
|
|
const iframe = document.getElementById('workflow-iframe');
|
|
iframe.onload = () => {
|
|
console.log('Iframe loaded successfully');
|
|
};
|
|
iframe.onerror = () => {
|
|
console.error('Iframe failed to load');
|
|
};
|
|
```
|
|
|
|
#### 2. Messages Not Received
|
|
|
|
**Problem**: Parent iframe not receiving completion messages
|
|
**Solution**: Verify event listener and origin settings
|
|
|
|
```javascript
|
|
// Debug message reception
|
|
window.addEventListener('message', (event) => {
|
|
console.log('Message received:', event.data);
|
|
// Your message handling logic
|
|
});
|
|
```
|
|
|
|
#### 3. Cross-Origin Issues
|
|
|
|
**Problem**: CORS errors when loading iframe
|
|
**Solution**: Configure proper CORS headers on the workflow server
|
|
|
|
```nginx
|
|
# Nginx configuration example
|
|
add_header Access-Control-Allow-Origin "*";
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
|
add_header Access-Control-Allow-Headers "Content-Type";
|
|
```
|
|
|
|
#### 4. URL Parameter Issues
|
|
|
|
**Problem**: URL parameters not working as expected
|
|
**Solution**: Verify parameter encoding
|
|
|
|
```javascript
|
|
// Proper URL construction
|
|
const baseUrl = '/workflow/process-id';
|
|
const params = new URLSearchParams({
|
|
debug: 'false',
|
|
hideComplete: 'true'
|
|
});
|
|
const fullUrl = `${baseUrl}?${params.toString()}`;
|
|
```
|
|
|
|
### Debug Checklist
|
|
|
|
- [ ] Iframe source URL is correct
|
|
- [ ] URL parameters are properly encoded
|
|
- [ ] Event listener is attached to window
|
|
- [ ] Origin validation is configured
|
|
- [ ] CORS headers are set on server
|
|
- [ ] Console shows no JavaScript errors
|
|
- [ ] Network tab shows successful iframe load
|
|
|
|
### Support
|
|
|
|
For additional support with iframe integration:
|
|
|
|
1. Check the browser console for error messages
|
|
2. Verify network connectivity to the workflow server
|
|
3. Test with different browsers
|
|
4. Review server logs for any backend issues
|
|
5. Contact support with specific error details
|
|
|
|
---
|
|
|
|
This documentation provides comprehensive guidance for integrating Corrad workflows into external applications. The iframe system is designed to be flexible, secure, and easy to implement while providing a seamless user experience. |