This repository has been archived on 2025-12-01. You can view files and clone it, but cannot push or open issues or pull requests.
Files
projectmycelium_old/src/views/dashboard/resource_provider.html

1156 lines
64 KiB
HTML

{% extends "dashboard/layout.html" %}
{% block title %}Mycelium Dashboard - Resource Provider{% endblock %}
{% block dashboard_content %}
<div class="my-4">
<h1>Resource Provider Dashboard</h1>
<p class="lead">Manage your nodes, configure slices, and monitor earnings</p>
<!-- Status Summary -->
<div class="row mt-4">
<div class="col-md-4">
<div class="stats-card success">
<h5 class="card-title">Active Nodes</h5>
<div class="d-flex justify-content-between align-items-end">
<h2 class="mb-0" id="active-nodes-count">{{ resource_provider_stats.online_nodes }}</h2>
<small class="text-muted">of {{ resource_provider_stats.total_nodes }} total</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="stats-card primary">
<h5 class="card-title">Allocated Slices</h5>
<div class="d-flex justify-content-between align-items-end">
<h2 class="mb-0" id="active-slices-count">{{ resource_provider_stats.allocated_base_slices }}</h2>
<small class="text-muted">of {{ resource_provider_stats.total_base_slices }} total</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="stats-card warning">
<h5 class="card-title">Monthly Earnings</h5>
<div class="d-flex justify-content-between align-items-end">
<h2 class="mb-0" id="monthly-earnings">{{ resource_provider_stats.monthly_earnings }}</h2>
<small class="text-muted">$/month</small>
</div>
</div>
</div>
</div>
<!-- Wallet & Staking Summary -->
<div class="row mt-4">
<div class="col-md-4">
<div class="stats-card secondary">
<h5 class="card-title">
<i class="bi bi-wallet2 me-2"></i>Wallet Balance
</h5>
<div class="d-flex justify-content-between align-items-end">
<h2 class="mb-0" id="wallet-balance">0</h2>
<small class="text-muted">Credits</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="stats-card success">
<h5 class="card-title">
<i class="bi bi-shield-check me-2"></i>Total Staked
</h5>
<div class="d-flex justify-content-between align-items-end">
<h2 class="mb-0" id="total-staked">0</h2>
<small class="text-muted">Credits</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="stats-card info">
<h5 class="card-title">
<i class="bi bi-nodes me-2"></i>Staked Nodes
</h5>
<div class="d-flex justify-content-between align-items-end">
<h2 class="mb-0" id="staked-nodes-count">0</h2>
<small class="text-muted">With Staking</small>
</div>
</div>
</div>
</div>
<!-- Add Node Button -->
<div class="row mt-4">
<div class="col-12">
<div class="d-flex justify-content-end">
<a href="#" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addNodeModal">
<i class="bi bi-plus-circle me-2"></i> Add Nodes
</a>
</div>
</div>
</div>
<!-- My Nodes Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>My Nodes</h3>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead>
<tr>
<th>Grid Node ID</th>
<th>Farm & Location</th>
<th>Specifications</th>
<th>SLA & Pricing</th>
<th>Certification</th>
<th>Status</th>
<th>Group</th>
<th>Staked</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="nodes-table-tbody">
<!-- Nodes will be populated by JavaScript -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Node Groups Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3>Node Groups</h3>
<button class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#createCustomNodeGroupModal">
<i class="bi bi-plus-circle me-2"></i> Create Custom Group
</button>
</div>
<div class="alert alert-info">
<h5><i class="bi bi-info-circle me-2"></i>Node Groups</h5>
<p class="mb-0">Organize your nodes into groups for easier management. Default groups (Compute, Storage, AI/GPU) are provided, or create custom groups for specific use cases.</p>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead>
<tr>
<th>Group Name</th>
<th>Type</th>
<th>Nodes</th>
<th>Total Resources</th>
<th>Status</th>
<th>Average Uptime</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="node-groups-table">
<tr>
<td colspan="7" class="text-center text-muted">
<div class="spinner-border spinner-border-sm me-2" role="status">
<span class="visually-hidden">Loading...</span>
</div>
Loading node groups...
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Automatic Slice Management Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3>Slice Management</h3>
<div class="d-flex gap-2">
<button class="btn btn-outline-info" data-action="slice.refresh">
<i class="bi bi-arrow-clockwise me-2"></i> Refresh Calculations
</button>
<button class="btn btn-outline-success" data-action="grid.sync">
<i class="bi bi-cloud-download me-2"></i> Sync with Grid
</button>
</div>
</div>
<div class="alert alert-info">
<h5><i class="bi bi-info-circle me-2"></i>Automatic Slice System</h5>
<p class="mb-0">Your nodes are automatically divided into <strong>base slices</strong> (1 vCPU + 4GB RAM + 200GB storage). Users can rent individual slices or combinations (2x, 4x, 8x). Slice availability updates in real-time based on node capacity.</p>
</div>
<!-- Slice Overview Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="stats-card primary">
<h6 class="card-title">Total Base Slices</h6>
<div class="d-flex justify-content-between align-items-end">
<h3 class="mb-0" id="total-base-slices">{{ resource_provider_stats.total_base_slices }}</h3>
<small class="text-muted">Available</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="stats-card success">
<h6 class="card-title">Allocated Slices</h6>
<div class="d-flex justify-content-between align-items-end">
<h3 class="mb-0" id="allocated-base-slices">{{ resource_provider_stats.allocated_base_slices }}</h3>
<small class="text-muted">Rented</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="stats-card info">
<h6 class="card-title">Available Slices</h6>
<div class="d-flex justify-content-between align-items-end">
<h3 class="mb-0" id="available-base-slices">{{ resource_provider_stats.available_base_slices }}</h3>
<small class="text-muted">For Rent</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="stats-card warning">
<h6 class="card-title">Utilization</h6>
<div class="d-flex justify-content-between align-items-end">
<h3 class="mb-0" id="slice-utilization">{{ resource_provider_stats.slice_utilization_percentage }}%</h3>
<small class="text-muted">Capacity</small>
</div>
</div>
</div>
</div>
<!-- Node Slice Breakdown Table -->
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead>
<tr>
<th>Node</th>
<th>Location</th>
<th>Total Slices</th>
<th>Allocated</th>
<th>Available</th>
<th>Slice Combinations</th>
<th>Pricing</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="node-slices-table">
{% if resource_provider_nodes %}
{% for node in resource_provider_nodes %}
<tr>
<td>
<div class="d-flex align-items-center">
<div class="node-status-indicator
{% if node.status == 'Online' %}bg-success{% else %}bg-danger{% endif %}
me-2"></div>
<div>
<strong>{{ node.name }}</strong>
{% if node.grid_node_id %}
<br><small class="text-muted">Grid ID: {{ node.grid_node_id }}</small>
{% endif %}
</div>
</div>
</td>
<td>
<span class="node-location" data-city="{{ node.grid_data.city | default(value='Unknown') }}" data-country="{{ node.grid_data.country | default(value='Unknown') }}">
{{ node.location }}
</span>
</td>
<td>
<span class="badge bg-primary">{{ node.total_base_slices }}</span>
</td>
<td>
<span class="badge bg-success">{{ node.allocated_base_slices }}</span>
</td>
<td>
<span class="badge bg-info">{{ node.total_base_slices - node.allocated_base_slices }}</span>
</td>
<td>
<div class="slice-combinations">
{% if node.available_combinations %}
{% for combo in node.available_combinations %}
{% if combo.quantity_available > 0 %}
<span class="badge bg-light text-dark me-1">
{{ combo.multiplier }}x ({{ combo.quantity_available }})
</span>
{% endif %}
{% endfor %}
{% else %}
<small class="text-muted">Calculating...</small>
{% endif %}
</div>
</td>
<td>
<div class="pricing-info">
{% if node.marketplace_sla %}
<strong>${{ node.marketplace_sla.base_slice_price }}/hr</strong>
<br><small class="text-muted">{{ node.marketplace_sla.uptime_guarantee_percentage | format_decimal(precision=1) }}% uptime</small>
{% else %}
<strong>${{ node.slice_pricing.base_price_per_hour }}/hr</strong>
<br><small class="text-muted">{{ node.uptime_percentage | format_decimal(precision=1) }}% uptime</small>
{% endif %}
</div>
</td>
<td>
{% if node.status == 'Online' %}
<span class="badge bg-success">Online</span>
{% else %}
<span class="badge bg-danger">{{ node.status }}</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="viewNodeDetails('{{ node.id }}')" title="View Details">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-outline-danger" onclick="deleteNodeConfiguration('{{ node.id }}')" title="Delete">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="9" class="text-center text-muted py-4">
<i class="bi bi-nodes display-4 d-block mb-3"></i>
<h5>No Nodes Added Yet</h5>
<p>Add your first node from the Mycelium Grid to start offering slices on the marketplace.</p>
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addNodeModal">
<i class="bi bi-plus-circle me-2"></i> Add Your First Node
</button>
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Capacity and Node Analytics Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>Capacity Analytics</h3>
<div class="row">
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header">
<h5>Capacity Utilization</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="capacityUtilizationChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header">
<h5>Node Status Overview</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="nodeStatusChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Earnings Monitoring Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>Earnings Monitoring</h3>
<div class="row">
<div class="col-md-8 mb-4">
<div class="card">
<div class="card-header">
<h5>Monthly Earnings Trend</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="earningsChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card">
<div class="card-header">
<h5>Earnings Breakdown</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="earningsBreakdownChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% block scripts %}
{{ super() }}
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
<!-- Load resource provider dashboard JavaScript -->
<script src="/static/js/dashboard-resource_provider.js"></script>
<style>
/* Ensure charts have consistent sizes */
.card-body {
min-height: 340px;
}
/* Slice Format Selection Styles */
.slice-format-card {
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid #e9ecef;
}
.slice-format-card:hover {
border-color: #007bff;
box-shadow: 0 0.125rem 0.25rem rgba(0, 123, 255, 0.075);
}
.slice-format-card.selected {
border-color: #007bff;
background-color: #f8f9ff;
box-shadow: 0 0.125rem 0.25rem rgba(0, 123, 255, 0.15);
}
.slice-format-checkbox {
transform: scale(1.2);
}
/* Node Management Modal Styles */
.node-management-tabs .nav-link {
border-radius: 0.375rem 0.375rem 0 0;
}
.node-management-tabs .nav-link.active {
background-color: #007bff;
border-color: #007bff;
color: white;
}
.slice-allocation-matrix {
max-height: 400px;
overflow-y: auto;
}
.allocation-status {
padding: 0.5rem;
border-radius: 0.375rem;
text-align: center;
font-size: 0.875rem;
}
.allocation-status.allocated {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.allocation-status.available {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
}
/* Enhanced pricing display styles */
.pricing-breakdown {
line-height: 1.3;
}
.pricing-breakdown small {
display: block;
margin-top: 2px;
}
/* Enhanced progress bar colors for utilization */
.progress-bar.bg-success {
background-color: #28a745 !important;
}
.progress-bar.bg-info {
background-color: #17a2b8 !important;
}
.progress-bar.bg-warning {
background-color: #ffc107 !important;
}
</style>
{% endblock %}
<!-- Delete Slice Confirmation Modal -->
<div class="modal fade" id="deleteSliceModal" tabindex="-1" aria-labelledby="deleteSliceModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteSliceModalLabel">
<i class="bi bi-exclamation-triangle text-warning me-2"></i>
Confirm Deletion
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-warning">
<i class="bi bi-info-circle me-2"></i>
<strong>Warning:</strong> This action cannot be undone.
</div>
<p>Are you sure you want to delete the slice configuration <strong id="deleteSliceName">""</strong>?</p>
<p class="text-muted small">This will remove the slice template and stop offering it on the marketplace. Any active rentals will continue until their expiration.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirmDeleteSliceBtn">
<i class="bi bi-trash me-2"></i>Delete Slice Configuration
</button>
</div>
</div>
</div>
</div>
<!-- Add Nodes Modal -->
<div class="modal fade" id="addNodeModal" tabindex="-1" aria-labelledby="addNodeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addNodeModalLabel">Add Nodes from Mycelium Grid</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
<strong>Mycelium Grid Integration:</strong> Enter the Grid Node IDs of your physical Mycelium Nodes. Node specifications and location will be automatically fetched from the Mycelium Grid.
</div>
<!-- Node Input Mode Selection -->
<div class="mb-4">
<label class="form-label">How many nodes do you want to add?</label>
<div class="btn-group w-100" role="group" aria-label="Node input mode">
<input type="radio" class="btn-check" name="nodeInputMode" id="singleNodeMode" value="single" checked>
<label class="btn btn-outline-primary" for="singleNodeMode">Single Node</label>
<input type="radio" class="btn-check" name="nodeInputMode" id="multipleNodeMode" value="multiple">
<label class="btn btn-outline-primary" for="multipleNodeMode">Multiple Nodes</label>
</div>
</div>
<!-- Single Node Input -->
<div id="singleNodeInput" class="mb-4">
<label for="gridNodeId" class="form-label">Mycelium Grid Node ID</label>
<input type="number" class="form-control" id="gridNodeId" placeholder="e.g., 8, 42, 1337" min="1">
<small class="text-muted">Enter the ID of your node on the Mycelium Grid</small>
</div>
<!-- Multiple Nodes Input -->
<div id="multipleNodeInput" class="mb-4" style="display: none;">
<label for="gridNodeIds" class="form-label">Mycelium Grid Node IDs</label>
<textarea class="form-control" id="gridNodeIds" rows="3" placeholder="Enter node IDs separated by commas or new lines&#10;e.g., 8, 42, 1337&#10;or one per line"></textarea>
<small class="text-muted">Enter multiple node IDs separated by commas or new lines</small>
</div>
<!-- Node Preview Section -->
<div id="nodePreview" class="mb-4" style="display: none;">
<h6>Node Preview</h6>
<div id="nodePreviewContent" class="border rounded p-3 bg-light">
<!-- Node preview will be populated here -->
</div>
</div>
<!-- Node Group Assignment -->
<div class="mb-4">
<label for="nodeGroup" class="form-label">Node Group</label>
<select class="form-select node-group-select" id="nodeGroup" name="node_group">
<option value="">Single (No Group)</option>
<!-- Groups will be populated dynamically -->
</select>
<small class="text-muted">Assign this node to a group for easier management. Default groups are provided, or you can create custom groups.</small>
</div>
<!-- Group Configuration (Legacy - Hidden) -->
<div class="mb-4" style="display: none;">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="createGroupToggle">
<label class="form-check-label" for="createGroupToggle">
<strong>Organize nodes into a group</strong>
</label>
</div>
<small class="text-muted">Group nodes together for easier management and shared slice pricing</small>
</div>
<!-- Automatic Slice Management Section -->
<div class="mb-4">
<div class="card">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-layers me-2"></i>Automatic Slice Management</h6>
</div>
<div class="card-body">
<div class="alert alert-success">
<i class="bi bi-check-circle me-2"></i>
<strong>Automatic Slice Calculation:</strong> Slices will be automatically calculated from your node's capacity using the standard base slice unit (1 vCPU + 4GB RAM + 200GB storage). All possible slice combinations (1x, 2x, 4x, 8x, 16x, etc.) will be available based on your node's total capacity.
</div>
<div class="mb-3">
<label class="form-label">Base Slice Pricing</label>
<div class="input-group">
<input type="number" class="form-control" id="baseSlicePrice" placeholder="0.50" min="0.10" max="2.00" step="0.01" value="0.50">
<span class="input-group-text">$/hour</span>
</div>
<small class="text-muted">Price per base slice (1 vCPU + 4GB RAM + 200GB storage). Range: $0.10 - $2.00/hour. Larger combinations will be priced proportionally.</small>
</div>
<div class="mb-3">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
<strong>How it works:</strong>
<ul class="mb-0 mt-2">
<li>Your node capacity will be automatically analyzed</li>
<li>Maximum number of base slices will be calculated</li>
<li>All possible slice combinations (2x, 4x, 8x, 16x, etc.) will be generated automatically based on capacity</li>
<li>All slices inherit your node's location, uptime, and bandwidth characteristics</li>
<li>Slices appear in marketplace immediately after node registration</li>
</ul>
</div>
</div>
<div class="mb-3">
<label class="form-label">Node SLA Configuration</label>
<div class="row">
<div class="col-md-6">
<label for="nodeUptimeSLA" class="form-label">Minimum Uptime SLA</label>
<select class="form-select" id="nodeUptimeSLA">
<option value="99.0">99.0%</option>
<option value="99.5">99.5%</option>
<option value="99.8" selected>99.8%</option>
<option value="99.9">99.9%</option>
<option value="99.95">99.95%</option>
</select>
<small class="text-muted">All slices inherit this uptime guarantee</small>
</div>
<div class="col-md-6">
<label for="nodeBandwidthSLA" class="form-label">Minimum Bandwidth</label>
<div class="input-group">
<input type="number" class="form-control" id="nodeBandwidthSLA" placeholder="100" min="10" value="100">
<span class="input-group-text">Mbps</span>
</div>
<small class="text-muted">Shared bandwidth across all slices</small>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enableStaking">
<label class="form-check-label" for="enableStaking">
<strong>Enable Staking</strong>
</label>
</div>
<small class="text-muted">Stake Credits on this node for slashing protection and network security</small>
</div>
<!-- Staking Configuration Section -->
<div id="stakingConfigSection" style="display: none;">
<div class="card mt-3 mb-3">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-shield-check me-2"></i>Node Staking Configuration</h6>
</div>
<div class="card-body">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
<strong>Staking Purpose:</strong> Stake Credits for slashing protection and network security. Staked amounts can be slashed if SLA requirements are not met.
</div>
<!-- Staking Amount -->
<div class="row mb-3">
<div class="col-md-6">
<label for="stakingAmount" class="form-label">Staking Amount ($)</label>
<input type="number" class="form-control" id="stakingAmount" placeholder="e.g., 100" min="1" step="1">
<small class="text-muted">Available Balance: $<span id="availableBalance">0</span></small>
</div>
<div class="col-md-6">
<label for="stakingPeriod" class="form-label">Staking Period</label>
<select class="form-select" id="stakingPeriod">
<option value="3">3 Months</option>
<option value="6">6 Months</option>
<option value="12" selected>12 Months</option>
<option value="24">24 Months</option>
</select>
</div>
</div>
<!-- Staking Options -->
<div class="row mb-3">
<div class="col-md-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="earlyWithdrawalAllowed" checked>
<label class="form-check-label" for="earlyWithdrawalAllowed">
Allow early withdrawal (25% penalty)
</label>
</div>
</div>
</div>
<!-- Multi-node staking configuration -->
<div id="multiNodeStakingOptions" style="display: none;">
<hr class="my-4">
<h6><i class="bi bi-layers me-2"></i>Multi-Node Staking Configuration</h6>
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
<strong>Multiple Nodes Detected:</strong> Choose how to apply staking across your nodes.
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="multiNodeStaking" id="sameStakingForAllNodes" value="same" checked>
<label class="form-check-label" for="sameStakingForAllNodes">
<strong>Apply same staking to all nodes</strong>
<small class="text-muted d-block">All nodes will use the staking configuration above</small>
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="multiNodeStaking" id="individualStaking" value="individual">
<label class="form-check-label" for="individualStaking">
<strong>Set individual staking per node</strong>
<small class="text-muted d-block">Configure different staking amounts for each node</small>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Group Configuration Section -->
<div id="groupConfigSection" class="mb-4" style="display: none;">
<div class="card">
<div class="card-header">
<h6 class="mb-0">Node Group Configuration</h6>
</div>
<div class="card-body">
<div class="mb-3">
<label for="groupName" class="form-label">Group Name</label>
<input type="text" class="form-control" id="groupName" placeholder="e.g., High Performance Cluster">
</div>
<div class="mb-3">
<label for="groupDescription" class="form-label">Description (Optional)</label>
<textarea class="form-control" id="groupDescription" rows="2" placeholder="Describe the purpose of this node group"></textarea>
</div>
</div>
</div>
</div>
<!-- Existing Group Selection -->
<div id="existingGroupSection" class="mb-4" style="display: none;">
<label for="existingGroup" class="form-label">Add to Existing Group</label>
<select class="form-select node-group-select" id="existingGroup">
<option value="">Select an existing group...</option>
<!-- Existing groups will be populated here -->
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="validateNodesBtn">Fetch Nodes</button>
<button type="button" class="btn btn-success" id="addNodesBtn" style="display: none;">Add Nodes</button>
</div>
</div>
</div>
</div>
<!-- Node Slice Preview Modal -->
<div class="modal fade" id="nodeSlicePreviewModal" tabindex="-1" aria-labelledby="nodeSlicePreviewModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="nodeSlicePreviewModalLabel">Node Slice Breakdown</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
<strong>Automatic Slice Calculation:</strong> Based on your node's capacity, the following slice combinations will be automatically available in the marketplace.
</div>
<div id="sliceBreakdownContent">
<!-- Slice breakdown will be populated here -->
<div class="text-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">Calculating slices...</span>
</div>
<p class="mt-2">Calculating available slice combinations...</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-action="slice.refresh">
<i class="bi bi-arrow-clockwise me-2"></i>Recalculate Slices
</button>
</div>
</div>
</div>
</div>
<!-- Create Custom Node Group Modal -->
<div class="modal fade" id="createCustomNodeGroupModal" tabindex="-1" aria-labelledby="createCustomNodeGroupModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createCustomNodeGroupModalLabel">Create Custom Node Group</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-info">
<h6><i class="bi bi-info-circle me-2"></i>Custom Node Groups</h6>
<p class="mb-0">Create custom groups for specific use cases beyond the default Compute, Storage, and AI/GPU groups. You can assign nodes to this group later.</p>
</div>
<form id="createCustomGroupForm">
<div class="mb-3">
<label for="customGroupName" class="form-label">Group Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="customGroupName" placeholder="e.g., High Performance Cluster" required>
</div>
<div class="mb-3">
<label for="customGroupDescription" class="form-label">Description</label>
<textarea class="form-control" id="customGroupDescription" rows="3" placeholder="Describe the purpose and characteristics of this group"></textarea>
</div>
<div class="mb-3">
<label for="resourceOptimization" class="form-label">Resource Optimization</label>
<select class="form-select" id="resourceOptimization">
<option value="balanced">Balanced - General purpose optimization</option>
<option value="performance">Performance - Optimized for high performance</option>
<option value="efficiency">Efficiency - Optimized for resource efficiency</option>
<option value="custom">Custom - Manual optimization settings</option>
</select>
</div>
<div class="mb-3">
<label for="groupSlicePricing" class="form-label">Group Slice Pricing Strategy</label>
<select class="form-select" id="groupSlicePricing">
<option value="individual">Individual Node Pricing - Each node sets its own slice pricing</option>
<option value="unified">Unified Group Pricing - All nodes in group use same slice pricing</option>
<option value="tiered">Tiered Pricing - Different pricing based on node performance tier</option>
</select>
<small class="text-muted">Determines how slice pricing is managed across nodes in this group</small>
</div>
<div class="mb-3">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="autoScaling">
<label class="form-check-label" for="autoScaling">
Enable Auto-scaling
</label>
</div>
<small class="text-muted">Automatically adjust resources based on demand</small>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" id="createCustomGroupBtn">Create Custom Group</button>
</div>
</div>
</div>
</div>
<!-- Node Management Modal -->
<div class="modal fade" id="manageNodeModal" tabindex="-1" aria-labelledby="manageNodeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="manageNodeModalLabel">Manage Node: <span id="nodeNameTitle"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Tabbed Interface -->
<ul class="nav nav-tabs node-management-tabs" role="tablist">
<li class="nav-item">
<button class="nav-link active" id="overview-tab" data-bs-toggle="tab" data-bs-target="#node-overview" type="button" role="tab">
<i class="bi bi-speedometer2 me-1"></i>Overview
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="configuration-tab" data-bs-toggle="tab" data-bs-target="#node-configuration" type="button" role="tab">
<i class="bi bi-gear me-1"></i>Configuration
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="slices-tab" data-bs-toggle="tab" data-bs-target="#node-slices" type="button" role="tab">
<i class="bi bi-grid-3x3-gap me-1"></i>Slice Allocation
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="performance-tab" data-bs-toggle="tab" data-bs-target="#node-performance" type="button" role="tab">
<i class="bi bi-graph-up me-1"></i>Performance
</button>
</li>
</ul>
<div class="tab-content mt-3">
<!-- Overview Tab -->
<div class="tab-pane fade show active" id="node-overview" role="tabpanel">
<div class="row">
<div class="col-md-6">
<h6>Node Status</h6>
<div class="mb-3">
<label for="nodeStatus" class="form-label">Current Status</label>
<select class="form-select" id="nodeStatus">
<option value="Online">Online</option>
<option value="Offline">Offline</option>
<option value="Maintenance">Maintenance</option>
<option value="Error">Error</option>
<option value="Standby">Standby</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Quick Actions</label>
<div class="d-grid gap-2">
<button class="btn btn-outline-warning" data-action="node.maintenance">
<i class="bi bi-tools me-1"></i>Enter Maintenance Mode
</button>
<button class="btn btn-outline-success" data-action="node.restart">
<i class="bi bi-arrow-clockwise me-1"></i>Restart Node
</button>
</div>
</div>
</div>
<div class="col-md-6">
<h6>Node Information</h6>
<table class="table table-sm">
<tr><td><strong>Node ID:</strong></td><td id="nodeIdDisplay"></td></tr>
<tr><td><strong>Location:</strong></td><td id="nodeLocationDisplay"></td></tr>
<tr><td><strong>Uptime:</strong></td><td id="nodeUptimeDisplay"></td></tr>
<tr><td><strong>Health Score:</strong></td><td id="nodeHealthDisplay"></td></tr>
<tr><td><strong>Last Seen:</strong></td><td id="nodeLastSeenDisplay"></td></tr>
</table>
</div>
</div>
</div>
<!-- Configuration Tab -->
<div class="tab-pane fade" id="node-configuration" role="tabpanel">
<form id="nodeConfigForm">
<div class="row">
<div class="col-md-6">
<h6>Basic Configuration</h6>
<div class="mb-3">
<label for="editNodeName" class="form-label">Node Name</label>
<input type="text" class="form-control" id="editNodeName">
</div>
<div class="mb-3">
<label for="editNodeLocation" class="form-label">Location</label>
<input type="text" class="form-control" id="editNodeLocation">
</div>
<div class="mb-3">
<label for="editNodeRegion" class="form-label">Region</label>
<select class="form-select" id="editNodeRegion">
<option value="US-East">US-East</option>
<option value="US-West">US-West</option>
<option value="EU-Central">EU-Central</option>
<option value="Asia-Pacific">Asia-Pacific</option>
</select>
</div>
</div>
<div class="col-md-6">
<h6>Hardware Specifications</h6>
<div class="mb-3">
<label for="editCpuCores" class="form-label">CPU Cores</label>
<input type="number" class="form-control" id="editCpuCores">
</div>
<div class="mb-3">
<label for="editMemoryGb" class="form-label">Memory (GB)</label>
<input type="number" class="form-control" id="editMemoryGb">
</div>
<div class="mb-3">
<label for="editStorageGb" class="form-label">Storage (GB)</label>
<input type="number" class="form-control" id="editStorageGb">
</div>
<div class="mb-3">
<label for="editBandwidthMbps" class="form-label">Bandwidth (Mbps)</label>
<input type="number" class="form-control" id="editBandwidthMbps">
</div>
</div>
</div>
</form>
</div>
<!-- Slice Allocation Tab -->
<div class="tab-pane fade" id="node-slices" role="tabpanel">
<div class="row">
<div class="col-12">
<h6>Current Slice Allocation</h6>
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
You can modify slice allocation when no slices are currently rented.
</div>
<div class="slice-allocation-matrix">
<h6>Available Slice Formats</h6>
<div class="row" id="nodeSliceFormats">
<!-- Slice formats will be loaded here -->
</div>
<div class="mt-4">
<h6>Capacity Utilization</h6>
<div class="row">
<div class="col-md-3">
<div class="text-center">
<div class="progress mb-2" style="height: 20px;">
<div class="progress-bar" id="cpuUtilBar" role="progressbar"></div>
</div>
<small>CPU Utilization</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center">
<div class="progress mb-2" style="height: 20px;">
<div class="progress-bar bg-info" id="memoryUtilBar" role="progressbar"></div>
</div>
<small>Memory Utilization</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center">
<div class="progress mb-2" style="height: 20px;">
<div class="progress-bar bg-warning" id="storageUtilBar" role="progressbar"></div>
</div>
<small>Storage Utilization</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center">
<div class="progress mb-2" style="height: 20px;">
<div class="progress-bar bg-success" id="bandwidthUtilBar" role="progressbar"></div>
</div>
<small>Bandwidth Utilization</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Performance Tab -->
<div class="tab-pane fade" id="node-performance" role="tabpanel">
<div class="row">
<div class="col-md-6">
<h6>Performance Metrics</h6>
<div class="card">
<div class="card-body">
<canvas id="nodePerformanceChart" width="400" height="200"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<h6>Earnings & Statistics</h6>
<table class="table table-sm">
<tr><td><strong>Today's Earnings:</strong></td><td id="nodeTodayEarnings">$0</td></tr>
<tr><td><strong>Monthly Earnings:</strong></td><td id="nodeMonthlyEarnings">$0</td></tr>
<tr><td><strong>Total Deployments:</strong></td><td id="nodeTotalDeployments">0</td></tr>
<tr><td><strong>Active Slices:</strong></td><td id="nodeActiveSlices">0</td></tr>
<tr><td><strong>Average Load:</strong></td><td id="nodeAverageLoad">0%</td></tr>
</table>
<div class="mt-3">
<h6>Health Alerts</h6>
<div id="nodeHealthAlerts">
<div class="alert alert-success alert-sm">
<i class="bi bi-check-circle me-1"></i>All systems operational
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="saveNodeManagementBtn"><i class="bi bi-check me-1"></i>Save Configuration</button>
</div>
</div>
</div>
</div>
<!-- Node Details Modal -->
<div class="modal fade" id="nodeDetailsModal" tabindex="-1" aria-labelledby="nodeDetailsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="nodeDetailsModalLabel">
<i class="bi bi-server me-2"></i>Node Details
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="nodeDetailsContent">
<!-- Node details will be populated here -->
<div class="text-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2">Loading node details...</p>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Node Delete Confirmation Modal -->
<div class="modal fade" id="deleteNodeModal" tabindex="-1" aria-labelledby="deleteNodeModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteNodeModalLabel">
<i class="bi bi-exclamation-triangle text-warning me-2"></i>
Confirm Node Removal
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger">
<i class="bi bi-exclamation-triangle me-2"></i>
<strong>Warning:</strong> This action cannot be undone.
</div>
<p>Are you sure you want to remove the node <strong id="deleteNodeName">""</strong> from your farm?</p>
<p class="text-muted small">This will:</p>
<ul class="text-muted small">
<li>Remove the node from your dashboard</li>
<li>Stop offering it on the marketplace</li>
<li>Cancel any pending slice offerings</li>
<li>Preserve any active rentals until their expiration</li>
</ul>
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
<strong>Note:</strong> For grid nodes, this only removes the node from your dashboard. The physical node remains on the Mycelium Grid.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirmDeleteNodeBtn">
<i class="bi bi-trash me-2"></i>Remove Node
</button>
</div>
</div>
</div>
</div>
{% endblock %}