Compare commits
	
		
			1 Commits
		
	
	
		
			main
			...
			developmen
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8c2276de79 | 
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										411
									
								
								actix_mvc_app/src/views/flows/index copy.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								actix_mvc_app/src/views/flows/index copy.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,411 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
 | 
			
		||||
{% block title %}Flows Dashboard{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <!-- Navigation Tabs -->
 | 
			
		||||
    <div class="row mb-3">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <ul class="nav nav-tabs">
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link active" href="/flows">Dashboard</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link" href="/flows/list">All Workflows</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link" href="/flows/my-flows">My Workflows</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link" href="/flows/create">Create Workflow</a>
 | 
			
		||||
                </li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Info Alert -->
 | 
			
		||||
    <div class="row mb-3">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="alert alert-info alert-dismissible fade show">
 | 
			
		||||
                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
 | 
			
		||||
                <h5><i class="bi bi-info-circle"></i> About Workflows</h5>
 | 
			
		||||
                <p>The workflow system helps you track and manage business processes across your organization. Create new workflows, monitor progress, and collaborate with team members to ensure smooth operations.</p>
 | 
			
		||||
                <div class="mt-2">
 | 
			
		||||
                    <a href="/flows/documentation" class="btn btn-sm btn-outline-primary"><i class="bi bi-book"></i> Read Documentation</a>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Dashboard Main Content -->
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
        <!-- Workflows with Pending Actions -->
 | 
			
		||||
        <div class="col-lg-9 mb-4">
 | 
			
		||||
            <div class="card h-100">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Workflows with Pending Actions</h5>
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <a href="/flows/pending" class="btn btn-sm btn-outline-primary">View All Pending</a>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    {% if flows and flows|length > 0 %}
 | 
			
		||||
                    <div class="table-responsive">
 | 
			
		||||
                        <table class="table table-hover align-middle">
 | 
			
		||||
                            <thead>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <th>Workflow</th>
 | 
			
		||||
                                    <th>Type</th>
 | 
			
		||||
                                    <th>Current Step</th>
 | 
			
		||||
                                    <th>Last Updated</th>
 | 
			
		||||
                                    <th>Owner</th>
 | 
			
		||||
                                    <th>Actions</th>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            </thead>
 | 
			
		||||
                            <tbody>
 | 
			
		||||
                                {% for flow in flows %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="d-flex align-items-center">
 | 
			
		||||
                                            <div class="flex-shrink-0 me-2">
 | 
			
		||||
                                                <div class="avatar bg-light text-primary rounded p-2">
 | 
			
		||||
                                                    <i class="bi bi-diagram-3"></i>
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div>
 | 
			
		||||
                                                <a href="/flows/{{ flow.id }}" class="text-decoration-none fw-medium">{{ flow.name }}</a>
 | 
			
		||||
                                                <div class="small text-muted">ID: {{ flow.id }}</div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td><span class="badge bg-info">{{ flow.flow_type }}</span></td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% if flow.current_step %}
 | 
			
		||||
                                            <span class="text-warning fw-medium">{{ flow.current_step.name }}</span>
 | 
			
		||||
                                            <div class="small text-muted">{{ flow.current_step.description }}</div>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <span class="text-muted">No pending step</span>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <span>{{ flow.updated_at | date(format="%Y-%m-%d") }}</span>
 | 
			
		||||
                                        {% if flow.status == 'Stuck' %}
 | 
			
		||||
                                            <div class="small text-danger">May need attention</div>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <div class="small text-muted">Last updated</div>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="d-flex align-items-center">
 | 
			
		||||
                                            <div class="flex-shrink-0 me-2">
 | 
			
		||||
                                                <div class="avatar avatar-sm">
 | 
			
		||||
                                                    <img src="{{ flow.owner_avatar or '/static/img/avatar-placeholder.png' }}" alt="{{ flow.owner_name }}" class="rounded-circle" onerror="this.src='/static/img/avatar-placeholder.png'; this.onerror='';">
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div>{{ flow.owner_name }}</div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="d-flex gap-2">
 | 
			
		||||
                                            <a href="/flows/{{ flow.id }}#take-action" class="btn btn-sm btn-primary">Take Action</a>
 | 
			
		||||
                                            <div class="dropdown">
 | 
			
		||||
                                                <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
 | 
			
		||||
                                                    <i class="bi bi-three-dots"></i>
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                                <ul class="dropdown-menu">
 | 
			
		||||
                                                    <li><a class="dropdown-item" href="/flows/{{ flow.id }}">View Details</a></li>
 | 
			
		||||
                                                    <li><a class="dropdown-item" href="/flows/{{ flow.id }}/reassign">Reassign</a></li>
 | 
			
		||||
                                                    <li><a class="dropdown-item" href="/flows/{{ flow.id }}/extend">Extend Deadline</a></li>
 | 
			
		||||
                                                </ul>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                    <div class="text-center py-5">
 | 
			
		||||
                        <i class="bi bi-check-circle-fill fs-1 text-success mb-3"></i>
 | 
			
		||||
                        <h5>No Pending Actions</h5>
 | 
			
		||||
                        <p class="text-muted">There are no workflows that require your immediate attention.</p>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        
 | 
			
		||||
        <!-- Timeline of Recent Flow Steps -->
 | 
			
		||||
        <div class="col-lg-3 mb-4">
 | 
			
		||||
            <div class="card h-100">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Recent Activity</h5>
 | 
			
		||||
                    <a href="/flows/activity" class="btn btn-sm btn-outline-primary">View All</a>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body p-0">
 | 
			
		||||
                    {% if flows and flows|length > 0 %}
 | 
			
		||||
                        <div class="list-group list-group-flush">
 | 
			
		||||
                            {% set count = 0 %}
 | 
			
		||||
                            {% for flow in flows %}
 | 
			
		||||
                                {% if count < 8 %}
 | 
			
		||||
                                {% set count = count + 1 %}
 | 
			
		||||
                                <div class="list-group-item border-start-0 border-end-0 py-3">
 | 
			
		||||
                                    <div class="d-flex">
 | 
			
		||||
                                        <div class="me-3">
 | 
			
		||||
                                            <div class="timeline-icon bg-light text-{% if flow.status == 'Completed' %}success{% elif flow.status == 'Stuck' %}danger{% else %}primary{% endif %} rounded-circle p-2">
 | 
			
		||||
                                                <i class="bi bi-{% if flow.status == 'Completed' %}check-circle{% elif flow.status == 'Stuck' %}exclamation-triangle{% else %}arrow-right-circle{% endif %} fs-5"></i>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                        <div class="flex-fill">
 | 
			
		||||
                                            <div class="d-flex justify-content-between align-items-center">
 | 
			
		||||
                                                <div class="fw-medium">
 | 
			
		||||
                                                    {% if flow.status == 'In Progress' %}Working on{% elif flow.status == 'Completed' %}Completed{% elif flow.status == 'Stuck' %}Stuck at{% else %}Updated{% endif %}
 | 
			
		||||
                                                    {% if flow.current_step %} {{ flow.current_step.name }}{% endif %}
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                                <div class="text-muted small">{{ flow.updated_at | date(format="%H:%M") }}</div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div>in <a href="/flows/{{ flow.id }}" class="text-decoration-none">{{ flow.name }}</a></div>
 | 
			
		||||
                                            <div class="text-muted small mt-1">by {{ flow.owner_name }}</div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="card-footer text-center">
 | 
			
		||||
                            <a href="/flows/activity" class="btn btn-sm btn-outline-secondary">View Full Activity Log</a>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <div class="list-group list-group-flush">
 | 
			
		||||
                            <div class="list-group-item border-start-0 border-end-0 py-5 text-center">
 | 
			
		||||
                                <i class="bi bi-hourglass fs-1 text-muted mb-3"></i>
 | 
			
		||||
                                <h6>No Recent Activity</h6>
 | 
			
		||||
                                <p class="text-muted small mb-0">Activity will appear here as workflows progress.</p>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Compact Filter Controls -->
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Workflow Filters</h5>
 | 
			
		||||
                    <button class="btn btn-sm btn-outline-primary" type="button" data-bs-toggle="collapse" data-bs-target="#filterCollapse" aria-expanded="false" aria-controls="filterCollapse">
 | 
			
		||||
                        <i class="bi bi-funnel"></i> Show/Hide Filters
 | 
			
		||||
                    </button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="collapse show" id="filterCollapse">
 | 
			
		||||
                    <div class="card-body">
 | 
			
		||||
                        <form class="row g-3" action="/flows" method="get">
 | 
			
		||||
                            <div class="col-md-3">
 | 
			
		||||
                                <label for="status" class="form-label">Status</label>
 | 
			
		||||
                                <select class="form-select" id="status" name="status">
 | 
			
		||||
                                    <option value="all" selected>All</option>
 | 
			
		||||
                                    <option value="in_progress">In Progress</option>
 | 
			
		||||
                                    <option value="completed">Completed</option>
 | 
			
		||||
                                    <option value="stuck">Stuck</option>
 | 
			
		||||
                                    <option value="cancelled">Cancelled</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <!-- Freezone filter - for UI demonstration only -->
 | 
			
		||||
                            <div class="col-md-3">
 | 
			
		||||
                                <label for="freezone" class="form-label">Freezone</label>
 | 
			
		||||
                                <select class="form-select" id="freezone" name="freezone" disabled>
 | 
			
		||||
                                    <option value="all" selected>All Freezones</option>
 | 
			
		||||
                                    <option value="dubai_multi_commodities_centre">DMCC</option>
 | 
			
		||||
                                    <option value="dubai_international_financial_centre">DIFC</option>
 | 
			
		||||
                                    <option value="jebel_ali_free_zone">JAFZA</option>
 | 
			
		||||
                                    <option value="dubai_silicon_oasis">DSO</option>
 | 
			
		||||
                                    <option value="dubai_internet_city">DIC</option>
 | 
			
		||||
                                    <option value="dubai_media_city">DMC</option>
 | 
			
		||||
                                    <option value="abu_dhabi_global_market">ADGM</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                                <div class="form-text">Coming soon</div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="col-md-3">
 | 
			
		||||
                                <label for="type" class="form-label">Workflow Type</label>
 | 
			
		||||
                                <select class="form-select" id="type" name="type">
 | 
			
		||||
                                    <option value="all" selected>All</option>
 | 
			
		||||
                                    <option value="company_registration">Company Incorporation</option>
 | 
			
		||||
                                    <option value="user_onboarding">KYC Verification</option>
 | 
			
		||||
                                    <option value="service_activation">License Activation</option>
 | 
			
		||||
                                    <option value="payment_processing">Payment Processing</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="col-md-3">
 | 
			
		||||
                                <label for="search" class="form-label">Search</label>
 | 
			
		||||
                                <input type="text" class="form-control" id="search" name="search" placeholder="Search workflows...">
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="col-12 text-end">
 | 
			
		||||
                                <button type="submit" class="btn btn-primary">
 | 
			
		||||
                                    <i class="bi bi-filter me-1"></i> Apply Filters
 | 
			
		||||
                                </button>
 | 
			
		||||
                                <a href="/flows" class="btn btn-outline-secondary">
 | 
			
		||||
                                    <i class="bi bi-x-circle me-1"></i> Clear Filters
 | 
			
		||||
                                </a>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Recent Active Workflows Section -->
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Active Workflows (Recent Updates)</h5>
 | 
			
		||||
                    <a href="/flows/list" class="btn btn-sm btn-outline-primary">View All</a>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    <div class="row">
 | 
			
		||||
                        {% set count = 0 %}
 | 
			
		||||
                        {% for flow in flows %}
 | 
			
		||||
                            {% if count < 3 and flow.status == 'In Progress' %}
 | 
			
		||||
                                <div class="col-md-4 mb-3">
 | 
			
		||||
                                    <div class="card h-100">
 | 
			
		||||
                                        <div class="card-body">
 | 
			
		||||
                                            <h5 class="card-title">{{ flow.name }}</h5>
 | 
			
		||||
                                            <h6 class="card-subtitle mb-2 text-muted">Owner: {{ flow.owner_name }}</h6>
 | 
			
		||||
                                            <div class="mb-3">
 | 
			
		||||
                                                <span class="badge bg-primary">{{ flow.flow_type }}</span>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <p class="mb-2">Current stage: 
 | 
			
		||||
                                                {% set current = flow.current_step %}
 | 
			
		||||
                                                {% if current %}
 | 
			
		||||
                                                {{ current.name }}
 | 
			
		||||
                                                {% else %}
 | 
			
		||||
                                                <span class="text-muted">No active stage</span>
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                            </p>
 | 
			
		||||
                                            <div class="progress mb-2" style="height: 20px;">
 | 
			
		||||
                                                <div class="progress-bar bg-primary" role="progressbar" 
 | 
			
		||||
                                                    style="width: {{ flow.progress_percentage }}%;" 
 | 
			
		||||
                                                    aria-valuenow="{{ flow.progress_percentage }}" 
 | 
			
		||||
                                                    aria-valuemin="0" aria-valuemax="100">{{ flow.progress_percentage }}%</div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div class="d-flex justify-content-between align-items-center mt-3">
 | 
			
		||||
                                                <small class="text-muted">Updated: {{ flow.updated_at | date(format="%Y-%m-%d") }}</small>
 | 
			
		||||
                                                <a href="/flows/{{ flow.id }}" class="btn btn-sm btn-outline-primary">View Details</a>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                {% set count = count + 1 %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                        {% if count == 0 %}
 | 
			
		||||
                            <div class="col-12 text-center py-4">
 | 
			
		||||
                                <i class="bi bi-clipboard-check fs-1 text-muted mb-3"></i>
 | 
			
		||||
                                <h5>No active workflows</h5>
 | 
			
		||||
                                <p class="text-muted">All workflows are either completed or not yet started.</p>
 | 
			
		||||
                                <a href="/flows/create" class="btn btn-primary mt-3">Create New Workflow</a>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Flows Table (Simplified) -->
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Recent Workflows</h5>
 | 
			
		||||
                    <a href="/flows/list" class="btn btn-sm btn-outline-primary">View All Workflows</a>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    {% if flows|length > 0 %}
 | 
			
		||||
                    <div class="table-responsive">
 | 
			
		||||
                        <table class="table table-hover">
 | 
			
		||||
                            <thead>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <th>Workflow Name</th>
 | 
			
		||||
                                    <th>Type</th>
 | 
			
		||||
                                    <th>Status</th>
 | 
			
		||||
                                    <th>Assignee</th>
 | 
			
		||||
                                    <th>Progress</th>
 | 
			
		||||
                                    <th>Initiated</th>
 | 
			
		||||
                                    <th>Last Updated</th>
 | 
			
		||||
                                    <th>Current Stage</th>
 | 
			
		||||
                                    <th>Actions</th>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            </thead>
 | 
			
		||||
                            <tbody>
 | 
			
		||||
                                {% for flow in flows %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <a href="/flows/{{ flow.id }}">{{ flow.name }}</a>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>{{ flow.flow_type }}</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <span
 | 
			
		||||
                                            class="badge {% if flow.status == 'In Progress' %}bg-primary{% elif flow.status == 'Completed' %}bg-success{% elif flow.status == 'Stuck' %}bg-danger{% else %}bg-secondary{% endif %}">
 | 
			
		||||
                                            {{ flow.status }}
 | 
			
		||||
                                        </span>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>{{ flow.owner_name }}</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="progress mb-2" style="height: 20px;">
 | 
			
		||||
                                            <div class="progress-bar {% if flow.status == 'Completed' %}bg-success{% elif flow.status == 'Stuck' %}bg-danger{% else %}bg-primary{% endif %}"
 | 
			
		||||
                                                role="progressbar" style="width: {{ flow.progress_percentage }}%;"
 | 
			
		||||
                                                aria-valuenow="{{ flow.progress_percentage }}" aria-valuemin="0"
 | 
			
		||||
                                                aria-valuemax="100">{{ flow.progress_percentage }}%</div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>{{ flow.created_at | date(format="%Y-%m-%d") }}</td>
 | 
			
		||||
                                    <td>{{ flow.updated_at | date(format="%Y-%m-%d") }}</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% set current = flow.current_step %}
 | 
			
		||||
                                        {% if current %}
 | 
			
		||||
                                        {{ current.name }}
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                        {% if flow.status == 'Completed' %}
 | 
			
		||||
                                        <span class="text-success">All stages completed</span>
 | 
			
		||||
                                        {% elif flow.status == 'Cancelled' %}
 | 
			
		||||
                                        <span class="text-secondary">Workflow cancelled</span>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                        <span class="text-muted">No active stage</span>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="btn-group">
 | 
			
		||||
                                            <a href="/flows/{{ flow.id }}" class="btn btn-sm btn-primary" title="View Details">
 | 
			
		||||
                                                <i class="bi bi-eye"></i>
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                            {% if flow.status == 'In Progress' %}
 | 
			
		||||
                                            <a href="/flows/{{ flow.id }}#advance" class="btn btn-sm btn-success" title="Advance to Next Stage">
 | 
			
		||||
                                                <i class="bi bi-arrow-right"></i>
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                    <div class="text-center py-4">
 | 
			
		||||
                        <i class="bi bi-search display-1 text-muted"></i>
 | 
			
		||||
                        <p class="lead mt-3">No workflows found matching your criteria.</p>
 | 
			
		||||
                        <p class="text-muted">Try adjusting your filters or create a new workflow.</p>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -3,122 +3,188 @@
 | 
			
		||||
{% block title %}Flows Dashboard{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
        <div class="col-md-8">
 | 
			
		||||
            <h1 class="display-5 mb-4">Flows Dashboard</h1>
 | 
			
		||||
            <p class="lead">Track and manage workflow processes across the organization.</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col-md-4 text-md-end">
 | 
			
		||||
            <a href="/flows/create" class="btn btn-primary">
 | 
			
		||||
                <i class="bi bi-plus-circle me-1"></i> Create New Workflow
 | 
			
		||||
            </a>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Statistics Cards -->
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
        <div class="col-md-3 mb-3">
 | 
			
		||||
            <div class="card text-white bg-primary h-100">
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    <h5 class="card-title">Total Flows</h5>
 | 
			
		||||
                    <p class="display-4">{{ stats.total_flows }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col-md-3 mb-3">
 | 
			
		||||
            <div class="card text-white bg-success h-100">
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    <h5 class="card-title">In Progress</h5>
 | 
			
		||||
                    <p class="display-4">{{ stats.in_progress_flows }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col-md-3 mb-3">
 | 
			
		||||
            <div class="card text-white bg-danger h-100">
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    <h5 class="card-title">Stuck</h5>
 | 
			
		||||
                    <p class="display-4">{{ stats.stuck_flows }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col-md-3 mb-3">
 | 
			
		||||
            <div class="card text-white bg-info h-100">
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    <h5 class="card-title">Completed</h5>
 | 
			
		||||
                    <p class="display-4">{{ stats.completed_flows }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Filter Controls -->
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
    <!-- Navigation Tabs -->
 | 
			
		||||
    <div class="row mb-3">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
                <div class="card-header">
 | 
			
		||||
                    <h5 class="mb-0">Filter Workflows</h5>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    <form class="row g-3" action="/flows" method="get">
 | 
			
		||||
                        <div class="col-md-3">
 | 
			
		||||
                            <label for="status" class="form-label">Status</label>
 | 
			
		||||
                            <select class="form-select" id="status" name="status">
 | 
			
		||||
                                <option value="all" selected>All</option>
 | 
			
		||||
                                <option value="in_progress">In Progress</option>
 | 
			
		||||
                                <option value="completed">Completed</option>
 | 
			
		||||
                                <option value="stuck">Stuck</option>
 | 
			
		||||
                                <option value="cancelled">Cancelled</option>
 | 
			
		||||
                            </select>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <!-- Freezone filter - for UI demonstration only -->
 | 
			
		||||
                        <div class="col-md-3">
 | 
			
		||||
                            <label for="freezone" class="form-label">Freezone</label>
 | 
			
		||||
                            <select class="form-select" id="freezone" name="freezone" disabled>
 | 
			
		||||
                                <option value="all" selected>All Freezones</option>
 | 
			
		||||
                                <option value="dubai_multi_commodities_centre">DMCC</option>
 | 
			
		||||
                                <option value="dubai_international_financial_centre">DIFC</option>
 | 
			
		||||
                                <option value="jebel_ali_free_zone">JAFZA</option>
 | 
			
		||||
                                <option value="dubai_silicon_oasis">DSO</option>
 | 
			
		||||
                                <option value="dubai_internet_city">DIC</option>
 | 
			
		||||
                                <option value="dubai_media_city">DMC</option>
 | 
			
		||||
                                <option value="abu_dhabi_global_market">ADGM</option>
 | 
			
		||||
                            </select>
 | 
			
		||||
                            <div class="form-text">Coming soon</div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-md-3">
 | 
			
		||||
                            <label for="type" class="form-label">Workflow Type</label>
 | 
			
		||||
                            <select class="form-select" id="type" name="type">
 | 
			
		||||
                                <option value="all" selected>All</option>
 | 
			
		||||
                                <option value="company_registration">Company Incorporation</option>
 | 
			
		||||
                                <option value="user_onboarding">KYC Verification</option>
 | 
			
		||||
                                <option value="service_activation">License Activation</option>
 | 
			
		||||
                                <option value="payment_processing">Payment Processing</option>
 | 
			
		||||
                            </select>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-md-3">
 | 
			
		||||
                            <label for="search" class="form-label">Search</label>
 | 
			
		||||
                            <input type="text" class="form-control" id="search" name="search" placeholder="Search workflows...">
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-12 text-end">
 | 
			
		||||
                            <button type="submit" class="btn btn-primary">
 | 
			
		||||
                                <i class="bi bi-filter me-1"></i> Apply Filters
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <a href="/flows" class="btn btn-outline-secondary">
 | 
			
		||||
                                <i class="bi bi-x-circle me-1"></i> Clear Filters
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
            <ul class="nav nav-tabs">
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link active" href="/flows">Dashboard</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link" href="/flows/list">All Workflows</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link" href="/flows/my-flows">My Workflows</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link" href="/flows/create">Create Workflow</a>
 | 
			
		||||
                </li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Info Alert -->
 | 
			
		||||
    <div class="row mb-3">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="alert alert-info alert-dismissible fade show">
 | 
			
		||||
                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
 | 
			
		||||
                <h5><i class="bi bi-info-circle"></i> About Workflows</h5>
 | 
			
		||||
                <p>The workflow system helps you track and manage business processes across your organization. Create new workflows, monitor progress, and collaborate with team members to ensure smooth operations.</p>
 | 
			
		||||
                <div class="mt-2">
 | 
			
		||||
                    <a href="/flows/documentation" class="btn btn-sm btn-outline-primary"><i class="bi bi-book"></i> Read Documentation</a>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Flows Table -->
 | 
			
		||||
    <!-- Dashboard Main Content -->
 | 
			
		||||
    <div class="row mb-4">
 | 
			
		||||
        <!-- Workflows with Pending Actions -->
 | 
			
		||||
        <div class="col-lg-9 mb-4">
 | 
			
		||||
            <div class="card h-100">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Workflows with Pending Actions</h5>
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <a href="/flows/pending" class="btn btn-sm btn-outline-primary">View All Pending</a>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    {% if flows and flows|length > 0 %}
 | 
			
		||||
                    <div class="table-responsive">
 | 
			
		||||
                        <table class="table table-hover align-middle">
 | 
			
		||||
                            <thead>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <th>Workflow</th>
 | 
			
		||||
                                    <th>Type</th>
 | 
			
		||||
                                    <th>Current Step</th>
 | 
			
		||||
                                    <th>Last Updated</th>
 | 
			
		||||
                                    <th>Actions</th>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            </thead>
 | 
			
		||||
                            <tbody>
 | 
			
		||||
                                {% for flow in flows %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="d-flex align-items-center">
 | 
			
		||||
                                            <div class="flex-shrink-0 me-2">
 | 
			
		||||
                                                <div class="avatar bg-light text-primary rounded p-2">
 | 
			
		||||
                                                    <i class="bi bi-diagram-3"></i>
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div>
 | 
			
		||||
                                                <a href="/flows/{{ flow.id }}" class="text-decoration-none fw-medium">{{ flow.name }}</a>
 | 
			
		||||
                                                <div class="small text-muted">ID: {{ flow.id }}</div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td><span class="badge bg-info">{{ flow.flow_type }}</span></td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% if flow.current_step %}
 | 
			
		||||
                                            <span class="text-warning fw-medium">{{ flow.current_step.name }}</span>
 | 
			
		||||
                                            <div class="small text-muted">{{ flow.current_step.description }}</div>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <span class="text-muted">No pending step</span>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <span>{{ flow.updated_at | date(format="%Y-%m-%d") }}</span>
 | 
			
		||||
                                        {% if flow.status == 'Stuck' %}
 | 
			
		||||
                                            <div class="small text-danger">May need attention</div>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <div class="small text-muted">Last updated</div>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="d-flex gap-2">
 | 
			
		||||
                                            <a href="/flows/{{ flow.id }}#take-action" class="btn btn-sm btn-primary">Take Action</a>
 | 
			
		||||
                                            <div class="dropdown">
 | 
			
		||||
                                                <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
 | 
			
		||||
                                                    <i class="bi bi-three-dots"></i>
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                                <ul class="dropdown-menu">
 | 
			
		||||
                                                    <li><a class="dropdown-item" href="/flows/{{ flow.id }}">View Details</a></li>
 | 
			
		||||
                                                    <li><a class="dropdown-item" href="/flows/{{ flow.id }}/reassign">Reassign</a></li>
 | 
			
		||||
                                                    <li><a class="dropdown-item" href="/flows/{{ flow.id }}/extend">Extend Deadline</a></li>
 | 
			
		||||
                                                </ul>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                    <div class="text-center py-5">
 | 
			
		||||
                        <i class="bi bi-check-circle-fill fs-1 text-success mb-3"></i>
 | 
			
		||||
                        <h5>No Pending Actions</h5>
 | 
			
		||||
                        <p class="text-muted">There are no workflows that require your immediate attention.</p>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        
 | 
			
		||||
        <!-- Timeline of Recent Flow Steps -->
 | 
			
		||||
        <div class="col-lg-3 mb-4">
 | 
			
		||||
            <div class="card h-100">
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Recent Activity</h5>
 | 
			
		||||
                    <a href="/flows/activity" class="btn btn-sm btn-outline-primary">View All</a>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body p-0">
 | 
			
		||||
                    {% if flows and flows|length > 0 %}
 | 
			
		||||
                        <div class="list-group list-group-flush">
 | 
			
		||||
                            {% set count = 0 %}
 | 
			
		||||
                            {% for flow in flows %}
 | 
			
		||||
                                {% if count < 8 %}
 | 
			
		||||
                                {% set count = count + 1 %}
 | 
			
		||||
                                <div class="list-group-item border-start-0 border-end-0 py-3">
 | 
			
		||||
                                    <div class="d-flex">
 | 
			
		||||
                                        <div class="me-3">
 | 
			
		||||
                                            <div class="timeline-icon bg-light text-{% if flow.status == 'Completed' %}success{% elif flow.status == 'Stuck' %}danger{% else %}primary{% endif %} rounded-circle p-2">
 | 
			
		||||
                                                <i class="bi bi-{% if flow.status == 'Completed' %}check-circle{% elif flow.status == 'Stuck' %}exclamation-triangle{% else %}arrow-right-circle{% endif %} fs-5"></i>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                        <div class="flex-fill">
 | 
			
		||||
                                            <div class="d-flex justify-content-between align-items-center">
 | 
			
		||||
                                                <div class="fw-medium">
 | 
			
		||||
                                                    {% if flow.status == 'In Progress' %}Working on{% elif flow.status == 'Completed' %}Completed{% elif flow.status == 'Stuck' %}Stuck at{% else %}Updated{% endif %}
 | 
			
		||||
                                                    {% if flow.current_step %} {{ flow.current_step.name }}{% endif %}
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                                <div class="text-muted small">{{ flow.updated_at | date(format="%H:%M") }}</div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div>in <a href="/flows/{{ flow.id }}" class="text-decoration-none">{{ flow.name }}</a></div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="card-footer text-center">
 | 
			
		||||
                            <a href="/flows/activity" class="btn btn-sm btn-outline-secondary">View Full Activity Log</a>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <div class="list-group list-group-flush">
 | 
			
		||||
                            <div class="list-group-item border-start-0 border-end-0 py-5 text-center">
 | 
			
		||||
                                <i class="bi bi-hourglass fs-1 text-muted mb-3"></i>
 | 
			
		||||
                                <h6>No Recent Activity</h6>
 | 
			
		||||
                                <p class="text-muted small mb-0">Activity will appear here as workflows progress.</p>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Flows Table (Simplified) -->
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
                <div class="card-header">
 | 
			
		||||
                    <h5 class="mb-0">All Workflows</h5>
 | 
			
		||||
                <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                    <h5 class="mb-0">Recent Workflows</h5>
 | 
			
		||||
                    <a href="/flows/list" class="btn btn-sm btn-outline-primary">View All Workflows</a>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="card-body">
 | 
			
		||||
                    {% if flows|length > 0 %}
 | 
			
		||||
@@ -129,7 +195,6 @@
 | 
			
		||||
                                    <th>Workflow Name</th>
 | 
			
		||||
                                    <th>Type</th>
 | 
			
		||||
                                    <th>Status</th>
 | 
			
		||||
                                    <th>Assignee</th>
 | 
			
		||||
                                    <th>Progress</th>
 | 
			
		||||
                                    <th>Initiated</th>
 | 
			
		||||
                                    <th>Last Updated</th>
 | 
			
		||||
@@ -150,7 +215,6 @@
 | 
			
		||||
                                            {{ flow.status }}
 | 
			
		||||
                                        </span>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>{{ flow.owner_name }}</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <div class="progress mb-2" style="height: 20px;">
 | 
			
		||||
                                            <div class="progress-bar {% if flow.status == 'Completed' %}bg-success{% elif flow.status == 'Stuck' %}bg-danger{% else %}bg-primary{% endif %}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								flowbroker/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										29
									
								
								flowbroker/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -972,6 +972,12 @@ version = "0.4.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "heck"
 | 
			
		||||
version = "0.5.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hermit-abi"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
@@ -994,6 +1000,8 @@ dependencies = [
 | 
			
		||||
 "rhai_wrapper",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "strum",
 | 
			
		||||
 "strum_macros",
 | 
			
		||||
 "tst",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -1750,7 +1758,7 @@ dependencies = [
 | 
			
		||||
name = "rhai_autobind_macros"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "heck",
 | 
			
		||||
 "heck 0.4.1",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
@@ -2045,6 +2053,25 @@ version = "1.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "strum"
 | 
			
		||||
version = "0.26.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "strum_macros"
 | 
			
		||||
version = "0.26.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "heck 0.5.0",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "rustversion",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "2.0.101"
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ use sigsocket::registry::ConnectionRegistry;
 | 
			
		||||
use log::{info, error};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
use rhai::{Engine, EvalAltResult, Position};
 | 
			
		||||
use serde_json::Value as JsonValue;
 | 
			
		||||
// use std::collections::HashMap; // Removed as no longer used
 | 
			
		||||
use heromodels; // Added for database models
 | 
			
		||||
use heromodels::db::hero::OurDB;
 | 
			
		||||
@@ -138,80 +137,70 @@ pub struct RhaiScriptFormData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// --- Handlers ---
 | 
			
		||||
 | 
			
		||||
// Display list of flows
 | 
			
		||||
async fn list_flows(data: web::Data<AppState>) -> ActixResult<HttpResponse> {
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    
 | 
			
		||||
    match data.db.collection::<Flow>() {
 | 
			
		||||
        Ok(flow_collection) => {
 | 
			
		||||
            match flow_collection.get_all() {
 | 
			
		||||
                Ok(mut flows_vec) => {
 | 
			
		||||
                    // Sort by creation date, newest first
 | 
			
		||||
                    flows_vec.sort_by(|a, b| b.base_data.created_at.cmp(&a.base_data.created_at));
 | 
			
		||||
                    context.insert("flows", &flows_vec);
 | 
			
		||||
                },
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    error!("Failed to retrieve flows from database: {:?}", e);
 | 
			
		||||
                    // Optionally, insert an empty vec or an error message for the template
 | 
			
		||||
                    context.insert("flows", &Vec::<Flow>::new()); 
 | 
			
		||||
                    context.insert("db_error", "Failed to load flows.");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            error!("Failed to get flow collection from database: {}", e);
 | 
			
		||||
            context.insert("flows", &Vec::<Flow>::new());
 | 
			
		||||
            context.insert("db_error", "Database collection error.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let rendered = data.templates.render("index.html", &context)
 | 
			
		||||
        .map_err(|e| {
 | 
			
		||||
            error!("Template error (index.html): {}", e);
 | 
			
		||||
            actix_web::error::ErrorInternalServerError("Template error rendering index.html")
 | 
			
		||||
        })?;
 | 
			
		||||
    Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Show form to create a new flow
 | 
			
		||||
#[derive(Serialize, Clone)] // Clone is for the context, Serialize for Tera
 | 
			
		||||
struct RhaiExampleScript {
 | 
			
		||||
// --- Context Structs for Templates ---
 | 
			
		||||
#[derive(Serialize, Clone)]
 | 
			
		||||
struct RhaiExampleDisplay {
 | 
			
		||||
    name: String,
 | 
			
		||||
    content: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn new_flow_form(data: web::Data<AppState>) -> impl Responder {
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    let mut example_scripts = Vec::new();
 | 
			
		||||
    let examples_path = PathBuf::from("templates/rhai_examples");
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
struct ListFlowsContext {
 | 
			
		||||
    flows: Vec<Flow>, // Using heromodels::models::flowbroker_models::Flow
 | 
			
		||||
    example_scripts: Vec<RhaiExampleDisplay>,
 | 
			
		||||
    error_message: Option<String>,
 | 
			
		||||
    success_message: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Handlers ---
 | 
			
		||||
 | 
			
		||||
// Display list of flows
 | 
			
		||||
async fn list_flows(data: web::Data<AppState>) -> ActixResult<HttpResponse> {
 | 
			
		||||
    let tera = &data.templates;
 | 
			
		||||
 | 
			
		||||
    // Fetch actual flows from the database
 | 
			
		||||
    let flows_collection = data.db.collection::<Flow>()
 | 
			
		||||
        .map_err(|e| actix_web::error::ErrorInternalServerError(format!("DB Error: Failed to get flows collection: {}", e)))?;
 | 
			
		||||
    let (mut flows, flow_error_message) = match flows_collection.get_all() {
 | 
			
		||||
        Ok(mut flows_vec) => {
 | 
			
		||||
            flows_vec.sort_by(|a, b| b.base_data.created_at.cmp(&a.base_data.created_at)); // Sort by newest
 | 
			
		||||
            (flows_vec, None)
 | 
			
		||||
        },
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            error!("Failed to fetch flows: {:?}", e);
 | 
			
		||||
            (Vec::new(), Some(format!("Error fetching flows: {:?}", e)))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Load Rhai example scripts
 | 
			
		||||
    let examples_path = PathBuf::from("templates/rhai_examples");
 | 
			
		||||
    let mut example_scripts_display = Vec::new();
 | 
			
		||||
    if examples_path.is_dir() {
 | 
			
		||||
        match std_fs::read_dir(examples_path) {
 | 
			
		||||
            Ok(entries) => {
 | 
			
		||||
                for entry in entries {
 | 
			
		||||
                    if let Ok(entry) = entry {
 | 
			
		||||
                        let path = entry.path();
 | 
			
		||||
                        if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("rhai") {
 | 
			
		||||
                        if path.is_file() && path.extension().and_then(std::ffi::OsStr::to_str) == Some("rhai") {
 | 
			
		||||
                            let file_stem = path.file_stem().and_then(std::ffi::OsStr::to_str).unwrap_or("Unknown Script");
 | 
			
		||||
                            // Convert filename (e.g., simple_two_step) to a nicer name (e.g., Simple Two Step)
 | 
			
		||||
                            let script_name = file_stem.replace("_", " ")
 | 
			
		||||
                                .split_whitespace()
 | 
			
		||||
                                .map(|word| {
 | 
			
		||||
                                    let mut c = word.chars();
 | 
			
		||||
                                    match c.next() {
 | 
			
		||||
                                        None => String::new(),
 | 
			
		||||
                                        Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
 | 
			
		||||
                                    }
 | 
			
		||||
                                })
 | 
			
		||||
                                .collect::<Vec<String>>().join(" ");
 | 
			
		||||
 | 
			
		||||
                            match std_fs::read_to_string(&path) {
 | 
			
		||||
                                Ok(content) => {
 | 
			
		||||
                                    let file_stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or("Unknown Script");
 | 
			
		||||
                                    // Convert filename (e.g., simple_two_step) to a nicer name (e.g., Simple Two Step)
 | 
			
		||||
                                    let name = file_stem.replace("_", " ")
 | 
			
		||||
                                        .split_whitespace()
 | 
			
		||||
                                        .map(|word| {
 | 
			
		||||
                                            let mut c = word.chars();
 | 
			
		||||
                                            match c.next() {
 | 
			
		||||
                                                None => String::new(),
 | 
			
		||||
                                                Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
 | 
			
		||||
                                            }
 | 
			
		||||
                                        })
 | 
			
		||||
                                        .collect::<Vec<String>>().join(" ");
 | 
			
		||||
                                    example_scripts.push(RhaiExampleScript { name, content });
 | 
			
		||||
                                    example_scripts_display.push(RhaiExampleDisplay { name: script_name, content });
 | 
			
		||||
                                }
 | 
			
		||||
                                Err(e) => {
 | 
			
		||||
                                    error!("Failed to read Rhai example script {}: {}", path.display(), e);
 | 
			
		||||
                                    error!("Failed to read Rhai example script {:?}: {}", path, e);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
@@ -219,20 +208,31 @@ async fn new_flow_form(data: web::Data<AppState>) -> impl Responder {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                error!("Failed to read rhai_examples directory: {}", e);
 | 
			
		||||
                error!("Failed to read Rhai examples directory: {}", e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    example_scripts_display.sort_by(|a, b| a.name.cmp(&b.name));
 | 
			
		||||
 | 
			
		||||
    context.insert("example_scripts", &example_scripts);
 | 
			
		||||
    info!("Rendering new flow form with {} examples from files.", example_scripts.len());
 | 
			
		||||
    match data.templates.render("new_flow_form.html", &context) {
 | 
			
		||||
        Ok(rendered) => HttpResponse::Ok().body(rendered),
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            error!("Template error in new_flow_form: {}", e);
 | 
			
		||||
            HttpResponse::InternalServerError().body(format!("Template error: {}", e))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let list_context = ListFlowsContext {
 | 
			
		||||
        flows,
 | 
			
		||||
        example_scripts: example_scripts_display,
 | 
			
		||||
        error_message: flow_error_message,
 | 
			
		||||
        success_message: None, // TODO: Populate from query params or session later if needed
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let tera_ctx = Context::from_serialize(&list_context).unwrap_or_else(|e| {
 | 
			
		||||
        error!("Failed to serialize ListFlowsContext: {}", e);
 | 
			
		||||
        // Fallback to a minimal context or an error state if serialization fails
 | 
			
		||||
        let mut err_ctx = Context::new();
 | 
			
		||||
        err_ctx.insert("error_message", &"Critical error preparing page data.".to_string());
 | 
			
		||||
        err_ctx
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Still rendering to index.html, which will be the revamped list_flows.html
 | 
			
		||||
    let rendered = tera.render("index.html", &tera_ctx)
 | 
			
		||||
        .map_err(|e| actix_web::error::ErrorInternalServerError(format!("Template error (index.html): {}", e)))?;
 | 
			
		||||
    Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handle creation of a new flow
 | 
			
		||||
@@ -646,7 +646,7 @@ pub fn configure_app_routes(cfg: &mut web::ServiceConfig) {
 | 
			
		||||
       .service(
 | 
			
		||||
            web::scope("/flows") // Group flow-related routes under /flows
 | 
			
		||||
                // .route("", web::get().to(list_flows)) // If you want /flows to also list flows
 | 
			
		||||
                .route("/new", web::get().to(new_flow_form))
 | 
			
		||||
                // .route("/new", web::get().to(new_flow_form)) // Deprecated, functionality merged into root list_flows
 | 
			
		||||
                .route("/create", web::post().to(create_flow))
 | 
			
		||||
                .route("/create_script", web::post().to(create_flow_from_script)) // Moved inside /flows scope
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -2,27 +2,316 @@
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <title>Flowbroker - Flows</title>
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
    <title>FlowBroker Dashboard</title>
 | 
			
		||||
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
 | 
			
		||||
          integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
 | 
			
		||||
    <!-- Optional: Link to your custom style.css if needed, but ensure it doesn't conflict heavily with Bootstrap -->
 | 
			
		||||
    <link rel="stylesheet" href="/static/style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>Active Flows</h1>
 | 
			
		||||
    <a href="/flows/new">Create New Flow</a>
 | 
			
		||||
    <div id="flows-list">
 | 
			
		||||
        {% if flows %}
 | 
			
		||||
            <ul>
 | 
			
		||||
                {% for flow in flows %}
 | 
			
		||||
                    <li>
 | 
			
		||||
                        <strong>{{ flow.name }}</strong> (UUID: {{ flow.flow_uuid }}) - Status: {{ flow.status }}
 | 
			
		||||
                        <br>
 | 
			
		||||
                        Created: {{ flow.base_data.created_at | date(format="%Y-%m-%d %H:%M:%S") }} <!-- Assuming created_at is a Unix timestamp -->
 | 
			
		||||
                        <p><a href="/flows/{{ flow.flow_uuid }}">View Details</a></p> <!-- Link uses flow_uuid -->
 | 
			
		||||
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
 | 
			
		||||
        <div class="container-fluid">
 | 
			
		||||
            <a class="navbar-brand" href="/">FlowBroker</a>
 | 
			
		||||
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
 | 
			
		||||
                    aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
 | 
			
		||||
                <span class="navbar-toggler-icon"></span>
 | 
			
		||||
            </button>
 | 
			
		||||
            <div class="collapse navbar-collapse" id="navbarNav">
 | 
			
		||||
                <ul class="navbar-nav">
 | 
			
		||||
                    <li class="nav-item">
 | 
			
		||||
                        <a class="nav-link active" aria-current="page" href="/">Dashboard</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </ul>
 | 
			
		||||
        {% else %}
 | 
			
		||||
            <p>No active flows. <a href="/flows/new">Create one?</a></p>
 | 
			
		||||
                    <!-- Add other nav items here later if needed -->
 | 
			
		||||
                </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </nav>
 | 
			
		||||
 | 
			
		||||
    <div class="container mt-4">
 | 
			
		||||
        {% if error_message %}
 | 
			
		||||
            <div class="alert alert-danger" role="alert">
 | 
			
		||||
                {{ error_message }}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        {% if success_message %}
 | 
			
		||||
            <div class="alert alert-success" role="alert">
 | 
			
		||||
                {{ success_message }}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        <h2>Active Flows</h2>
 | 
			
		||||
        <div id="flows-list" class="mb-4">
 | 
			
		||||
            {% if flows %}
 | 
			
		||||
                <table class="table table-hover">
 | 
			
		||||
                    <thead>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <th scope="col">Name</th>
 | 
			
		||||
                            <th scope="col">UUID</th>
 | 
			
		||||
                            <th scope="col">Status</th>
 | 
			
		||||
                            <th scope="col">Created At</th>
 | 
			
		||||
                            <th scope="col">Actions</th>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody>
 | 
			
		||||
                        {% for flow in flows %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>{{ flow.name }}</td>
 | 
			
		||||
                                <td><small>{{ flow.flow_uuid }}</small></td>
 | 
			
		||||
                                <td><span class="badge bg-secondary">{{ flow.status | default(value="Unknown") }}</span></td>
 | 
			
		||||
                                <td>{{ flow.base_data.created_at | date(format="%Y-%m-%d %H:%M:%S") }}</td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <a href="/flows/{{ flow.flow_uuid }}" class="btn btn-sm btn-primary">View</a>
 | 
			
		||||
                                    <button class="btn btn-sm btn-success run-flow-btn" data-flow-uuid="{{ flow.flow_uuid }}">Run</button>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <p>No active flows found. You can create one below.</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <hr class="my-4">
 | 
			
		||||
 | 
			
		||||
        <h2>Runnable Example Scripts</h2>
 | 
			
		||||
        <div id="example-scripts-list" class="mb-4">
 | 
			
		||||
            {% if example_scripts %}
 | 
			
		||||
                <ul class="list-group">
 | 
			
		||||
                    {% for example in example_scripts %}
 | 
			
		||||
                        <li class="list-group-item d-flex justify-content-between align-items-center">
 | 
			
		||||
                            {{ example.name }}
 | 
			
		||||
                            <button class="btn btn-sm btn-info load-script-btn" data-script-content="{{ example.content | escape }}">Load into Form</button>
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </ul>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <p>No example scripts found.</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <hr class="my-4">
 | 
			
		||||
 | 
			
		||||
        <div class="row">
 | 
			
		||||
            <div class="col-md-6 mb-4">
 | 
			
		||||
                <h2>Create New Flow from Rhai Script</h2>
 | 
			
		||||
                <div id="create-from-rhai-section">
 | 
			
		||||
                    <form action="/flows/create_script" method="POST">
 | 
			
		||||
                        <div class="mb-3">
 | 
			
		||||
                            <label for="rhai_script_content" class="form-label">Rhai Script:</label>
 | 
			
		||||
                            <textarea class="form-control" id="rhai_script_content" name="rhai_script" rows="10" placeholder="Enter your Rhai script here or select an example using the 'Load into Form' buttons above..."></textarea>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <button type="submit" class="btn btn-primary">Create Flow from Script</button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-md-6 mb-4">
 | 
			
		||||
                <h2>Create New Flow (Step-by-Step UI)</h2>
 | 
			
		||||
                <div id="create-step-by-step-section">
 | 
			
		||||
                    <form id="createFlowForm_dynamic" action="/flows/create" method="post">
 | 
			
		||||
                        <div class="mb-3">
 | 
			
		||||
                            <label for="flow_name_dynamic" class="form-label">Flow Name:</label>
 | 
			
		||||
                            <input type="text" id="flow_name_dynamic" name="flow_name" class="form-control" required>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <hr>
 | 
			
		||||
 | 
			
		||||
                        <div id="stepsContainer_dynamic" class="mb-3">
 | 
			
		||||
                            <!-- Steps will be added here by JavaScript -->
 | 
			
		||||
                            <p class="text-muted"><em>Steps will appear here as you add them.</em></p>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                        <button type="button" id="addStepBtn_dynamic" class="btn btn-secondary mb-3">Add Step</button>
 | 
			
		||||
                        <hr>
 | 
			
		||||
                        <button type="submit" class="btn btn-primary">Create Flow (Step-by-Step)</button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Templates for Dynamic Step-by-Step Form -->
 | 
			
		||||
    <template id="stepTemplate_dynamic">
 | 
			
		||||
        <div class="step card mb-3" data-step-index="">
 | 
			
		||||
            <div class="card-header d-flex justify-content-between align-items-center">
 | 
			
		||||
                <h5 class="mb-0">Step <span class="step-number"></span></h5>
 | 
			
		||||
                <button type="button" class="btn btn-danger btn-sm removeStepBtn_dynamic">Remove This Step</button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="card-body">
 | 
			
		||||
                <div class="mb-3">
 | 
			
		||||
                    <label class="form-label">Step Description (Optional):</label>
 | 
			
		||||
                    <input type="text" name="steps[X].description" class="form-control step-description_dynamic">
 | 
			
		||||
                </div>
 | 
			
		||||
                <h6>Signature Requirements for Step <span class="step-number"></span>:</h6>
 | 
			
		||||
                <div class="requirementsContainer_dynamic ps-3" data-step-index="">
 | 
			
		||||
                    <!-- Requirements will be added here by JS -->
 | 
			
		||||
                    <p class="text-muted small"><em>Requirements for this step will appear here.</em></p>
 | 
			
		||||
                </div>
 | 
			
		||||
                <button type="button" class="btn btn-outline-secondary btn-sm addRequirementBtn_dynamic mt-2" data-step-index="">Add Signature Requirement</button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </template>
 | 
			
		||||
 | 
			
		||||
    <template id="requirementTemplate_dynamic">
 | 
			
		||||
        <div class="requirement card mb-2" data-req-index="">
 | 
			
		||||
            <div class="card-body">
 | 
			
		||||
                <div class="d-flex justify-content-between align-items-center mb-2">
 | 
			
		||||
                    <strong>Requirement <span class="req-number"></span></strong>
 | 
			
		||||
                    <button type="button" class="btn btn-danger btn-sm removeRequirementBtn_dynamic">Remove Requirement</button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="mb-2">
 | 
			
		||||
                    <label class="form-label">Message to Sign:</label>
 | 
			
		||||
                    <textarea name="steps[X].requirements[Y].message" rows="2" class="form-control req-message_dynamic" required></textarea>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label class="form-label">Required Public Key:</label>
 | 
			
		||||
                    <input type="text" name="steps[X].requirements[Y].public_key" class="form-control req-pubkey_dynamic" required>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </template>
 | 
			
		||||
    <!-- End of Templates -->
 | 
			
		||||
 | 
			
		||||
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
 | 
			
		||||
            integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
 | 
			
		||||
            crossorigin="anonymous"></script>
 | 
			
		||||
    <script>
 | 
			
		||||
        // Basic script to handle 'Load into Form' for example scripts
 | 
			
		||||
        document.addEventListener('DOMContentLoaded', function () {
 | 
			
		||||
            const loadScriptButtons = document.querySelectorAll('.load-script-btn');
 | 
			
		||||
            loadScriptButtons.forEach(button => {
 | 
			
		||||
                button.addEventListener('click', function () {
 | 
			
		||||
                    const scriptContent = this.dataset.scriptContent;
 | 
			
		||||
                    const rhaiTextarea = document.querySelector('#rhai_script_content'); // Assuming this ID for the textarea
 | 
			
		||||
                    if (rhaiTextarea) {
 | 
			
		||||
                        rhaiTextarea.value = scriptContent;
 | 
			
		||||
                        // Optionally, scroll to the form or give some visual feedback
 | 
			
		||||
                        rhaiTextarea.focus();
 | 
			
		||||
                        alert('Script loaded into the textarea below!');
 | 
			
		||||
                    } else {
 | 
			
		||||
                        alert('Rhai script textarea not found on the page. It will be added soon.');
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // --- Start of Dynamic Step-by-Step Form Logic ---
 | 
			
		||||
            const stepsContainer_dynamic = document.getElementById('stepsContainer_dynamic');
 | 
			
		||||
            const addStepBtn_dynamic = document.getElementById('addStepBtn_dynamic');
 | 
			
		||||
            const stepTemplate_dynamic = document.getElementById('stepTemplate_dynamic');
 | 
			
		||||
            const requirementTemplate_dynamic = document.getElementById('requirementTemplate_dynamic');
 | 
			
		||||
            const form_dynamic = document.getElementById('createFlowForm_dynamic');
 | 
			
		||||
 | 
			
		||||
            if (stepsContainer_dynamic && addStepBtn_dynamic && stepTemplate_dynamic && requirementTemplate_dynamic && form_dynamic) { // Check if all elements exist
 | 
			
		||||
 | 
			
		||||
                const updateIndices_dynamic = () => {
 | 
			
		||||
                    const steps = stepsContainer_dynamic.querySelectorAll('.step'); // .step is the class on the root of the cloned template
 | 
			
		||||
                    steps.forEach((step, stepIdx) => {
 | 
			
		||||
                        step.dataset.stepIndex = stepIdx;
 | 
			
		||||
                        step.querySelector('.step-number').textContent = stepIdx + 1;
 | 
			
		||||
                        const descriptionInput = step.querySelector('.step-description_dynamic');
 | 
			
		||||
                        if(descriptionInput) descriptionInput.name = `steps[${stepIdx}].description`;
 | 
			
		||||
                        
 | 
			
		||||
                        const addReqBtn = step.querySelector('.addRequirementBtn_dynamic');
 | 
			
		||||
                        if (addReqBtn) addReqBtn.dataset.stepIndex = stepIdx;
 | 
			
		||||
 | 
			
		||||
                        const requirements = step.querySelectorAll('.requirementsContainer_dynamic .requirement'); // .requirement is class on root of its template
 | 
			
		||||
                        requirements.forEach((req, reqIdx) => {
 | 
			
		||||
                            req.dataset.reqIndex = reqIdx;
 | 
			
		||||
                            req.querySelector('.req-number').textContent = reqIdx + 1;
 | 
			
		||||
                            const messageTextarea = req.querySelector('.req-message_dynamic');
 | 
			
		||||
                            if(messageTextarea) messageTextarea.name = `steps[${stepIdx}].requirements[${reqIdx}].message`;
 | 
			
		||||
                            const pubkeyInput = req.querySelector('.req-pubkey_dynamic');
 | 
			
		||||
                            if(pubkeyInput) pubkeyInput.name = `steps[${stepIdx}].requirements[${reqIdx}].public_key`;
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                    // Remove the initial placeholder message if steps are present
 | 
			
		||||
                    const placeholder = stepsContainer_dynamic.querySelector('p.text-muted');
 | 
			
		||||
                    if (steps.length > 0 && placeholder) {
 | 
			
		||||
                        placeholder.style.display = 'none';
 | 
			
		||||
                    } else if (steps.length === 0 && placeholder) {
 | 
			
		||||
                        placeholder.style.display = 'block';
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                const addRequirement_dynamic = (currentStepElement, stepIndex) => {
 | 
			
		||||
                    const requirementsContainer = currentStepElement.querySelector('.requirementsContainer_dynamic');
 | 
			
		||||
                    if (!requirementsContainer) return;
 | 
			
		||||
                    const reqFragment = requirementTemplate_dynamic.content.cloneNode(true);
 | 
			
		||||
                    const newRequirement = reqFragment.querySelector('.requirement'); // .requirement is class on root
 | 
			
		||||
                     // Remove placeholder from requirements container if it exists
 | 
			
		||||
                    const reqPlaceholder = requirementsContainer.querySelector('p.text-muted.small');
 | 
			
		||||
                    if (reqPlaceholder) reqPlaceholder.style.display = 'none';
 | 
			
		||||
 | 
			
		||||
                    requirementsContainer.appendChild(newRequirement);
 | 
			
		||||
                    updateIndices_dynamic();
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                const addStep_dynamic = () => {
 | 
			
		||||
                    const stepFragment = stepTemplate_dynamic.content.cloneNode(true);
 | 
			
		||||
                    const newStep = stepFragment.querySelector('.step'); // .step is class on root
 | 
			
		||||
                    stepsContainer_dynamic.appendChild(newStep);
 | 
			
		||||
                    
 | 
			
		||||
                    const currentStepIndex = stepsContainer_dynamic.querySelectorAll('.step').length - 1;
 | 
			
		||||
                    addRequirement_dynamic(newStep, currentStepIndex); // Add one requirement by default
 | 
			
		||||
                    
 | 
			
		||||
                    updateIndices_dynamic();
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                stepsContainer_dynamic.addEventListener('click', (event) => {
 | 
			
		||||
                    if (event.target.classList.contains('removeStepBtn_dynamic')) {
 | 
			
		||||
                        event.target.closest('.step').remove();
 | 
			
		||||
                        if (stepsContainer_dynamic.querySelectorAll('.step').length === 0) {
 | 
			
		||||
                            // addStep_dynamic(); // Optionally re-add a step if all are removed
 | 
			
		||||
                        }
 | 
			
		||||
                        updateIndices_dynamic();
 | 
			
		||||
                    } else if (event.target.classList.contains('addRequirementBtn_dynamic')) {
 | 
			
		||||
                        const stepElement = event.target.closest('.step');
 | 
			
		||||
                        const stepIndex = parseInt(stepElement.dataset.stepIndex, 10);
 | 
			
		||||
                        addRequirement_dynamic(stepElement, stepIndex);
 | 
			
		||||
                    } else if (event.target.classList.contains('removeRequirementBtn_dynamic')) {
 | 
			
		||||
                        const requirementElement = event.target.closest('.requirement');
 | 
			
		||||
                        const stepElement = event.target.closest('.step');
 | 
			
		||||
                        const requirementsContainer = stepElement.querySelector('.requirementsContainer_dynamic');
 | 
			
		||||
                        requirementElement.remove();
 | 
			
		||||
                        
 | 
			
		||||
                        if (requirementsContainer.querySelectorAll('.requirement').length === 0) {
 | 
			
		||||
                            // const stepIndex = parseInt(stepElement.dataset.stepIndex, 10);
 | 
			
		||||
                            // addRequirement_dynamic(stepElement, stepIndex); // Optionally re-add a requirement
 | 
			
		||||
                             const reqPlaceholder = requirementsContainer.querySelector('p.text-muted.small');
 | 
			
		||||
                             if (reqPlaceholder) reqPlaceholder.style.display = 'block'; // Show placeholder if no reqs
 | 
			
		||||
                        }
 | 
			
		||||
                        updateIndices_dynamic();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                addStepBtn_dynamic.addEventListener('click', addStep_dynamic);
 | 
			
		||||
 | 
			
		||||
                // Add one step by default when the page loads, if no steps already (e.g. from server-side render)
 | 
			
		||||
                if (stepsContainer_dynamic.children.length === 1 && stepsContainer_dynamic.firstElementChild.tagName === 'P') { // Only placeholder present
 | 
			
		||||
                    addStep_dynamic();
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                form_dynamic.addEventListener('submit', (event) => {
 | 
			
		||||
                    if (stepsContainer_dynamic.querySelectorAll('.step').length === 0) {
 | 
			
		||||
                        alert('Please add at least one step to the flow.');
 | 
			
		||||
                        event.preventDefault();
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    const steps = stepsContainer_dynamic.querySelectorAll('.step');
 | 
			
		||||
                    for (let i = 0; i < steps.length; i++) {
 | 
			
		||||
                        if (steps[i].querySelectorAll('.requirementsContainer_dynamic .requirement').length === 0) {
 | 
			
		||||
                            alert(`Step ${i + 1} must have at least one signature requirement.`);
 | 
			
		||||
                            event.preventDefault();
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                console.warn('One or more elements for the dynamic step-by-step form were not found. JS not initialized.');
 | 
			
		||||
            }
 | 
			
		||||
            // --- End of Dynamic Step-by-Step Form Logic ---
 | 
			
		||||
        });
 | 
			
		||||
    </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user