init projectmycelium

This commit is contained in:
mik-tf
2025-09-01 21:37:01 -04:00
commit b41efb0e99
319 changed files with 128160 additions and 0 deletions

View File

@@ -0,0 +1,303 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Application Solutions{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>Application Solutions</h1>
<p class="lead">Discover self-healing applications that maintain sovereignty while offering convenience.</p>
<!-- Application Solutions Introduction -->
<div class="alert alert-info mb-4">
<div class="d-flex">
<div class="me-3">
<i class="bi bi-info-circle-fill fs-3"></i>
</div>
<div>
<h5 class="alert-heading">How Application Solutions Work</h5>
<p>ThreeFold Application Solutions use a unique model: You provide the compute resources (Slices), while solution providers manage the applications. This ensures you maintain sovereignty over your infrastructure while benefiting from professional management.</p>
<hr>
<p class="mb-0">When you deploy an application, you'll be guided through the process of allocating the necessary resources if you don't already have them.</p>
</div>
</div>
</div>
<!-- Filter and Search Section -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Filter Applications</h5>
<form class="row g-3" method="GET">
<div class="col-md-3">
<label for="appTypeFilter" class="form-label">App Type</label>
<select name="app_type" id="appTypeFilter" class="form-select">
<option value="">All Types</option>
<option value="Web">Web Applications</option>
<option value="Productivity">Productivity</option>
<option value="Development">Development</option>
<option value="Database">Database</option>
<option value="Communication">Communication</option>
<option value="CMS">Content Management</option>
</select>
</div>
<div class="col-md-3">
<label for="deploymentFilter" class="form-label">Deployment Type</label>
<select name="deployment_type" id="deploymentFilter" class="form-select">
<option value="">Any</option>
<option value="Container">Container</option>
<option value="VM">Virtual Machine</option>
<option value="Kubernetes">Kubernetes</option>
</select>
</div>
<div class="col-md-3">
<label for="minPriceFilter" class="form-label">Min Price ($)</label>
<input type="number" name="min_price" id="minPriceFilter" class="form-control"
value="" placeholder="0" min="0" step="1">
</div>
<div class="col-md-3">
<label for="maxPriceFilter" class="form-label">Max Price ($)</label>
<input type="number" name="max_price" id="maxPriceFilter" class="form-control"
value="" placeholder="50" min="0" step="1">
</div>
<div class="col-md-6 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
<a href="/marketplace/applications" class="btn btn-outline-secondary">Clear</a>
</div>
</form>
</div>
</div>
<!-- Applications Grid -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 mb-5">
{% if application_products and application_products | length > 0 %}
{% for product_data in application_products %}
<div class="col">
<div class="card h-100">
<div class="position-relative p-4 bg-light text-center">
<i class="bi bi-app fs-1 text-primary"></i>
<span class="position-absolute top-0 end-0 badge bg-primary m-2">{{ product_data.product.category_id | title }}</span>
</div>
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<h5 class="card-title">
<a href="/products/{{ product_data.product.id }}" class="text-decoration-none text-dark">
{{ product_data.product.name }}
</a>
</h5>
{% if product_data.product.metadata.featured %}
<span class="badge bg-warning">Featured</span>
{% elif product_data.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product_data.product.availability }}</span>
{% endif %}
</div>
<p class="card-text">{{ product_data.product.description | truncate(length=100) }}</p>
<!-- Features (from tags) -->
{% if product_data.product.metadata.tags and product_data.product.metadata.tags | length > 0 %}
<div class="mb-3">
<h6 class="mb-2">Features:</h6>
<div class="d-flex flex-wrap">
{% for tag in product_data.product.metadata.tags %}
<span class="badge bg-light text-dark me-1 mb-1">{{ tag | title }}</span>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Resource Requirements -->
{% if product_data.product.attributes %}
<div class="mb-3">
<h6 class="mb-2">Resource Requirements:</h6>
<div class="d-flex">
{% if product_data.product.attributes.cpu_cores %}
<div class="me-3"><i class="bi bi-cpu me-1"></i> {{ product_data.product.attributes.cpu_cores.value }} cores</div>
{% endif %}
{% if product_data.product.attributes.memory_gb %}
<div class="me-3"><i class="bi bi-memory me-1"></i> {{ product_data.product.attributes.memory_gb.value }} GB</div>
{% endif %}
{% if product_data.product.attributes.storage_gb %}
<div><i class="bi bi-hdd me-1"></i> {{ product_data.product.attributes.storage_gb.value }} GB</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Provider Info -->
<div class="mb-3">
<small class="text-muted">
<i class="bi bi-building me-1"></i>{{ product_data.product.provider_name }}
{% if product_data.product.metadata.location %}
<span class="ms-2">
<i class="bi bi-geo-alt me-1"></i>{{ product_data.product.metadata.location }}
</span>
{% endif %}
</small>
</div>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-center">
<div class="price-info">
<span class="text-muted small">Management fee:</span>
<div class="fw-bold text-primary">{{ product_data.formatted_price }}</div>
</div>
<div class="btn-group">
<button class="btn btn-success btn-sm buy-now-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}"
data-category="applications">
<i class="bi bi-lightning-fill me-1"></i>Buy Now
</button>
<button class="btn btn-outline-primary btn-sm add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
<a href="/products/{{ product_data.product.id }}" class="btn btn-outline-secondary btn-sm">View Details</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="text-center py-5">
<i class="bi bi-app display-1 text-muted"></i>
<h4 class="mt-3">No Applications Available</h4>
<p class="text-muted">Check back later for new application solutions.</p>
</div>
</div>
{% endif %}
</div>
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<nav aria-label="Application pages" class="mt-4">
<ul class="pagination justify-content-center">
<!-- Previous Page -->
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
{% if pagination.has_previous %}
<a class="page-link" href="?page={{ pagination.previous_page }}">Previous</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Previous</span>
{% endif %}
</li>
<!-- Page 1 -->
<li class="page-item {% if pagination.current_page == 0 %}active{% endif %}">
{% if pagination.current_page == 0 %}
<span class="page-link">1</span>
{% else %}
<a class="page-link" href="?page=0">1</a>
{% endif %}
</li>
<!-- Page 2 (if exists) -->
{% if pagination.total_pages > 1 %}
<li class="page-item {% if pagination.current_page == 1 %}active{% endif %}">
{% if pagination.current_page == 1 %}
<span class="page-link">2</span>
{% else %}
<a class="page-link" href="?page=1">2</a>
{% endif %}
</li>
{% endif %}
<!-- Page 3 (if exists) -->
{% if pagination.total_pages > 2 %}
<li class="page-item {% if pagination.current_page == 2 %}active{% endif %}">
{% if pagination.current_page == 2 %}
<span class="page-link">3</span>
{% else %}
<a class="page-link" href="?page=2">3</a>
{% endif %}
</li>
{% endif %}
<!-- Page 4 (if exists) -->
{% if pagination.total_pages > 3 %}
<li class="page-item {% if pagination.current_page == 3 %}active{% endif %}">
{% if pagination.current_page == 3 %}
<span class="page-link">4</span>
{% else %}
<a class="page-link" href="?page=3">4</a>
{% endif %}
</li>
{% endif %}
<!-- Page 5 (if exists) -->
{% if pagination.total_pages > 4 %}
<li class="page-item {% if pagination.current_page == 4 %}active{% endif %}">
{% if pagination.current_page == 4 %}
<span class="page-link">5</span>
{% else %}
<a class="page-link" href="?page=4">5</a>
{% endif %}
</li>
{% endif %}
<!-- Next Page -->
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
{% if pagination.has_next %}
<a class="page-link" href="?page={{ pagination.next_page }}">Next</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Next</span>
{% endif %}
</li>
</ul>
</nav>
<!-- Results Info -->
<div class="text-center text-muted mt-2">
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
({{ pagination.total_count }} total applications)
</div>
{% endif %}
<!-- How It Works Section -->
<div class="card">
<div class="card-body">
<h3 class="card-title">How Application Deployment Works</h3>
<div class="row">
<div class="col-md-6">
<h5>1. Choose Your Application</h5>
<p>Browse our catalog of self-healing applications and select the one that meets your needs.</p>
<h5>2. Resource Allocation</h5>
<p>We'll guide you through allocating the necessary compute resources (Slices) for your application.</p>
</div>
<div class="col-md-6">
<h5>3. Deployment & Management</h5>
<p>The solution provider handles deployment, updates, and maintenance while you retain full sovereignty.</p>
<h5>4. Access & Control</h5>
<p>Access your application through secure channels while maintaining complete control over your data and infrastructure.</p>
</div>
</div>
<div class="alert alert-success mt-3">
<i class="bi bi-shield-check me-2"></i>
<strong>Your Sovereignty Guaranteed:</strong> You own the infrastructure, we provide the expertise. Your data never leaves your control.
</div>
</div>
</div>
</div>
<style>
.price-info {
text-align: left;
}
.card-footer {
border-top: 1px solid rgba(0,0,0,.125);
}
</style>
<script src="/static/js/marketplace-category.js"></script>
{% endblock %}

View File

@@ -0,0 +1,410 @@
{% extends "marketplace/layout.html" %}
{% block title %}Shopping Cart - Project Mycelium{% endblock %}
{% block extra_css %}
<style>
.hover-shadow:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
transform: translateY(-1px);
}
.add-recommended-btn {
transition: all 0.2s ease;
min-width: 60px;
}
.add-recommended-btn:hover {
transform: scale(1.05);
}
</style>
{% endblock %}
{% block marketplace_content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><i class="bi bi-cart3 me-2"></i>Shopping Cart</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<a href="/marketplace" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Continue Shopping
</a>
</div>
</div>
</div>
{% if cart.is_empty %}
<!-- Empty Cart State -->
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card border-0">
<div class="card-body py-5">
<i class="bi bi-cart-x display-1 text-muted mb-4"></i>
<h3 class="text-muted mb-3">Your cart is empty</h3>
<p class="text-muted mb-4">Looks like you haven't added any items to your cart yet. Browse our marketplace to find the perfect ThreeFold resources for your needs.</p>
<a href="/marketplace" class="btn btn-primary btn-lg">
<i class="bi bi-shop me-2"></i>Browse Marketplace
</a>
</div>
</div>
</div>
</div>
{% else %}
<!-- Cart with Items -->
<div class="row">
<!-- Cart Items -->
<div class="col-lg-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Cart Items ({{ cart.item_count }})</h5>
<button class="btn btn-sm btn-outline-danger" id="clearCartBtn">
<i class="bi bi-trash me-1"></i>Clear Cart
</button>
</div>
<div class="card-body p-0">
{% for item in cart.items %}
<div class="cart-item border-bottom p-4" data-product-id="{{ item.product_id }}">
<div class="row align-items-center">
<!-- Product Info -->
<div class="col-md-6">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="width: 60px; height: 60px;">
{% if item.product_category == "compute" %}
<i class="bi bi-cpu fs-4 text-primary"></i>
{% elif item.product_category == "hardware" %}
<i class="bi bi-hdd-rack fs-4 text-success"></i>
{% elif item.product_category == "gateways" %}
<i class="bi bi-globe fs-4 text-info"></i>
{% elif item.product_category == "applications" %}
<i class="bi bi-app fs-4 text-warning"></i>
{% elif item.product_category == "services" %}
<i class="bi bi-person-workspace fs-4 text-secondary"></i>
{% else %}
<i class="bi bi-box fs-4 text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-1">{{ item.product_name }}</h6>
<p class="text-muted mb-1 small">{{ item.provider_name }}</p>
<span class="badge bg-light text-dark">{{ item.product_category|title }}</span>
{% if item.specifications %}
<div class="mt-2">
<small class="text-muted">Specifications:</small>
<div class="mt-1">
{% for key, value in item.specifications %}
<span class="badge bg-secondary me-1">{{ key }}: {{ value }}</span>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Quantity Controls -->
<div class="col-md-3">
<div class="d-flex align-items-center justify-content-center">
<button class="btn btn-sm btn-outline-secondary" data-product-id="{{ item.product_id }}" data-action="decrease">
<i class="bi bi-dash"></i>
</button>
<span class="mx-3 fw-bold">{{ item.quantity }}</span>
<button class="btn btn-sm btn-outline-secondary" data-product-id="{{ item.product_id }}" data-action="increase">
<i class="bi bi-plus"></i>
</button>
</div>
</div>
<!-- Price and Actions -->
<div class="col-md-3">
<div class="text-end">
<div class="mb-2">
<small class="text-muted">Unit: {{ item.unit_price }}</small>
</div>
<div class="fw-bold text-primary mb-2">{{ item.total_price }}</div>
<button class="btn btn-sm btn-outline-danger" data-product-id="{{ item.product_id }}" data-action="remove">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Cart Summary -->
<div class="col-lg-4">
<div class="card sticky-top" style="top: 100px;">
<div class="card-header">
<h5 class="mb-0">Order Summary</h5>
</div>
<div class="card-body">
<!-- Currency Selector -->
<div class="mb-3">
<label class="form-label small text-muted">Display Currency</label>
<select class="form-select form-select-sm" id="currencySelector">
{% for currency in currencies %}
<option value="{{ currency.code }}" {% if currency.code == user_currency %}selected{% endif %}>
{{ currency.symbol }} {{ currency.name }}
</option>
{% endfor %}
</select>
</div>
<hr>
<!-- Price Breakdown -->
<div class="d-flex justify-content-between mb-2">
<span>Subtotal ({{ cart.item_count }} items)</span>
<span>{{ cart.subtotal }}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Estimated fees</span>
<span class="text-muted">Calculated at checkout</span>
</div>
<hr>
<div class="d-flex justify-content-between mb-3">
<span class="fw-bold">Total</span>
<span class="fw-bold text-primary fs-5">{{ cart.total }}</span>
</div>
<!-- Guest User Notice -->
{% if not user_json %}
<div class="alert alert-info mb-3">
<div class="d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<div>
<strong>Almost ready to checkout!</strong><br>
<small>You'll need to log in or create an account to complete your purchase. Don't worry - your cart items will be saved!</small>
</div>
</div>
</div>
{% endif %}
<!-- Checkout Button -->
<div class="d-grid">
{% if user_json %}
<a href="/checkout" class="btn btn-primary btn-lg">
<i class="bi bi-credit-card me-2"></i>Proceed to Checkout
</a>
{% else %}
<button class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#guestCheckoutModal">
<i class="bi bi-credit-card me-2"></i>Proceed to Checkout
</button>
{% endif %}
</div>
<!-- Security Notice -->
<div class="mt-3 p-3 bg-light rounded">
<div class="d-flex align-items-center">
<i class="bi bi-shield-check text-success me-2"></i>
<small class="text-muted">Secure checkout with 256-bit SSL encryption</small>
</div>
</div>
</div>
</div>
<!-- Recommended Products -->
<div class="card mt-4">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-stars me-2"></i>Recommended for you</h6>
</div>
<div class="card-body">
<div class="row g-2">
{% for product_data in recommended_products %}
<div class="col-12">
<div class="d-flex align-items-center p-3 border rounded hover-shadow" style="transition: all 0.2s ease;">
<i class="bi bi-cpu text-primary me-3 fs-4"></i>
<div class="flex-grow-1">
<div class="fw-bold">{{ product_data.product.name }}</div>
<small class="text-muted">{{ product_data.product.description }}</small>
<div class="text-primary fw-bold mt-1">{{ product_data.formatted_price }}</div>
</div>
<button class="btn btn-sm btn-primary add-recommended-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-product-price="{{ product_data.price.display_amount }}"
data-product-category="{{ product_data.product.category_id }}">
<i class="bi bi-plus me-1"></i>Add
</button>
</div>
</div>
{% endfor %}
</div>
<div class="text-center mt-3">
<a href="/marketplace" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-shop me-1"></i>Browse All Products
</a>
</div>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Clear Cart Confirmation Modal -->
<div class="modal fade" id="clearCartModal" tabindex="-1" aria-labelledby="clearCartModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="clearCartModalLabel">
<i class="bi bi-exclamation-triangle me-2"></i>Clear Entire Cart
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center mb-3">
<i class="bi bi-cart-x text-danger me-3" style="font-size: 2rem;"></i>
<div>
<p class="mb-1 fw-bold">Are you sure you want to clear your entire cart?</p>
<p class="mb-0 text-muted small">This action will remove all items from your cart and cannot be undone.</p>
</div>
</div>
<div class="alert alert-warning d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<small>All {{ cart.item_count }} items will be permanently removed from your cart.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i>Cancel
</button>
<button type="button" class="btn btn-danger" id="confirmClearCartBtn">
<i class="bi bi-trash me-1"></i>Clear Cart
</button>
</div>
</div>
</div>
</div>
<!-- Remove Item Confirmation Modal -->
<div class="modal fade" id="removeItemModal" tabindex="-1" aria-labelledby="removeItemModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="removeItemModalLabel">
<i class="bi bi-exclamation-triangle me-2"></i>Remove Item
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center mb-3">
<i class="bi bi-trash text-warning me-3" style="font-size: 2rem;"></i>
<div>
<p class="mb-1 fw-bold">Remove this item from your cart?</p>
<p class="mb-0 text-muted small">This action cannot be undone.</p>
</div>
</div>
<div class="alert alert-info d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<small>You can always add this item back later from the marketplace.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i>Cancel
</button>
<button type="button" class="btn btn-warning" id="confirmRemoveItemBtn">
<i class="bi bi-trash me-1"></i>Remove Item
</button>
</div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div id="loadingOverlay" class="position-fixed top-0 start-0 w-100 h-100 d-none" style="background: rgba(0,0,0,0.5); z-index: 9999;">
<div class="d-flex justify-content-center align-items-center h-100">
<div class="spinner-border text-light" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="/static/js/cart-marketplace.js"></script>
{% endblock %}
<!-- Guest Checkout Modal -->
<div class="modal fade" id="guestCheckoutModal" tabindex="-1" aria-labelledby="guestCheckoutModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="guestCheckoutModalLabel">
<i class="bi bi-person-check me-2"></i>Complete Your Purchase
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center mb-4">
<i class="bi bi-cart-check text-primary" style="font-size: 3rem;"></i>
<h4 class="mt-3 mb-2">Almost there!</h4>
<p class="text-muted">You're just one step away from completing your purchase.</p>
</div>
<div class="alert alert-info d-flex align-items-center mb-4">
<i class="bi bi-shield-check me-2"></i>
<div>
<strong>Your cart is safe!</strong><br>
<small>All items in your cart will be automatically saved when you log in or create an account.</small>
</div>
</div>
<div class="row g-3">
<div class="col-12">
<h6 class="mb-3">Choose an option to continue:</h6>
</div>
<div class="col-md-6">
<div class="card h-100 border-primary">
<div class="card-body text-center">
<i class="bi bi-person-fill text-primary mb-2" style="font-size: 2rem;"></i>
<h6>Already have an account?</h6>
<p class="text-muted small mb-3">Sign in to access your saved information and complete checkout quickly.</p>
<button type="button" class="btn btn-primary w-100" id="guestLoginBtn">
<i class="bi bi-box-arrow-in-right me-2"></i>Log In
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 border-success">
<div class="card-body text-center">
<i class="bi bi-person-plus-fill text-success mb-2" style="font-size: 2rem;"></i>
<h6>New to ThreeFold?</h6>
<p class="text-muted small mb-3">Create a free account to manage your orders and access exclusive features.</p>
<button type="button" class="btn btn-success w-100" id="guestRegisterBtn">
<i class="bi bi-person-plus me-2"></i>Create Account
</button>
</div>
</div>
</div>
</div>
<div class="mt-4 p-3 bg-light rounded">
<div class="row align-items-center">
<div class="col-auto">
<i class="bi bi-lightning-charge text-warning" style="font-size: 1.5rem;"></i>
</div>
<div class="col">
<h6 class="mb-1">Quick & Secure</h6>
<small class="text-muted">Registration takes less than 30 seconds. Your payment information is protected with enterprise-grade security.</small>
</div>
</div>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
<i class="bi bi-arrow-left me-2"></i>Continue Shopping
</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,499 @@
{% extends "base.html" %}
{% block title %}Shopping Cart - Project Mycelium{% endblock %}
{% block head %}
<!-- Add Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css">
<style>
.cart-item {
transition: all 0.3s ease;
}
.cart-item:hover {
background-color: #f8f9fa;
}
.quantity-controls {
display: flex;
align-items: center;
gap: 0.5rem;
}
.quantity-controls button {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.product-icon {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 8px;
}
.cart-summary {
position: sticky;
top: 100px;
}
.security-notice {
background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%);
border: 1px solid #b6d4da;
}
.empty-cart-icon {
font-size: 4rem;
color: #6c757d;
}
.loading-overlay {
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
}
.toast {
z-index: 10000;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/marketplace">Marketplace</a></li>
<li class="breadcrumb-item active" aria-current="page">Shopping Cart</li>
</ol>
</nav>
<!-- Page Header -->
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><i class="bi bi-cart3 me-2 text-primary"></i>Shopping Cart</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<a href="/marketplace" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Continue Shopping
</a>
</div>
</div>
</div>
{% if cart.is_empty %}
<!-- Empty Cart State -->
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card border-0 shadow-sm">
<div class="card-body py-5">
<i class="bi bi-cart-x empty-cart-icon mb-4"></i>
<h3 class="text-muted mb-3">Your cart is empty</h3>
<p class="text-muted mb-4">Looks like you haven't added any items to your cart yet. Browse our marketplace to find the perfect ThreeFold resources for your needs.</p>
<a href="/marketplace" class="btn btn-primary btn-lg">
<i class="bi bi-shop me-2"></i>Browse Marketplace
</a>
</div>
</div>
</div>
</div>
{% else %}
<!-- Cart with Items -->
<div class="row">
<!-- Cart Items -->
<div class="col-lg-8">
<div class="card shadow-sm">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-bag me-2"></i>Cart Items ({{ cart.item_count }})</h5>
<button class="btn btn-sm btn-outline-danger" id="clearCartBtn">
<i class="bi bi-trash me-1"></i>Clear Cart
</button>
</div>
<div class="card-body p-0">
{% for item in cart.items %}
<div class="cart-item border-bottom p-4" data-product-id="{{ item.product_id }}">
<div class="row align-items-center">
<!-- Product Info -->
<div class="col-md-6">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="product-icon">
{% if item.product_category == "compute" %}
<i class="bi bi-cpu fs-4 text-primary"></i>
{% elif item.product_category == "hardware" %}
<i class="bi bi-hdd-rack fs-4 text-success"></i>
{% elif item.product_category == "gateways" %}
<i class="bi bi-globe fs-4 text-info"></i>
{% elif item.product_category == "applications" %}
<i class="bi bi-app fs-4 text-warning"></i>
{% elif item.product_category == "services" %}
<i class="bi bi-person-workspace fs-4 text-secondary"></i>
{% else %}
<i class="bi bi-box fs-4 text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-1 fw-bold">{{ item.product_name }}</h6>
<p class="text-muted mb-1 small">
<i class="bi bi-building me-1"></i>{{ item.provider_name }}
</p>
<span class="badge bg-light text-dark border">
<i class="bi bi-tag me-1"></i>{{ item.product_category|title }}
</span>
{% if item.specifications %}
<div class="mt-2">
<small class="text-muted fw-bold">Specifications:</small>
<div class="mt-1">
{% for key, value in item.specifications %}
<span class="badge bg-secondary me-1 small">{{ key }}: {{ value }}</span>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Quantity Controls -->
<div class="col-md-3">
<div class="quantity-controls justify-content-center">
<button class="btn btn-sm btn-outline-secondary" data-product-id="{{ item.product_id }}" data-action="decrease">
<i class="bi bi-dash"></i>
</button>
<span class="mx-3 fw-bold fs-5">{{ item.quantity }}</span>
<button class="btn btn-sm btn-outline-secondary" data-product-id="{{ item.product_id }}" data-action="increase">
<i class="bi bi-plus"></i>
</button>
</div>
<div class="text-center mt-2">
<small class="text-muted">Quantity</small>
</div>
</div>
<!-- Price and Actions -->
<div class="col-md-3">
<div class="text-end">
<div class="mb-2">
<small class="text-muted">Unit: {{ item.unit_price }}</small>
</div>
<div class="fw-bold text-primary mb-3 fs-5">{{ item.total_price }}</div>
<button class="btn btn-sm btn-outline-danger" data-product-id="{{ item.product_id }}" data-action="remove" title="Remove item">
<i class="bi bi-trash"></i> Remove
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Cart Summary -->
<div class="col-lg-4">
<div class="card shadow-sm cart-summary">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="bi bi-receipt me-2"></i>Order Summary</h5>
</div>
<div class="card-body">
<!-- Currency Selector -->
<div class="mb-3">
<label class="form-label small text-muted fw-bold">
<i class="bi bi-currency-exchange me-1"></i>Display Currency
</label>
<select class="form-select form-select-sm" id="currencySelector">
{% for currency in currencies %}
<option value="{{ currency.code }}" {% if currency.code == user_currency %}selected{% endif %}>
{{ currency.symbol }} {{ currency.name }}
</option>
{% endfor %}
</select>
</div>
<hr>
<!-- Price Breakdown -->
<div class="d-flex justify-content-between mb-2">
<span><i class="bi bi-bag me-1"></i>Subtotal ({{ cart.item_count }} items)</span>
<span class="fw-bold">{{ cart.subtotal }}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted"><i class="bi bi-calculator me-1"></i>Estimated fees</span>
<span class="text-muted">Calculated at checkout</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted"><i class="bi bi-truck me-1"></i>Deployment</span>
<span class="text-success">Free</span>
</div>
<hr>
<div class="d-flex justify-content-between mb-3">
<span class="fw-bold fs-5">Total</span>
<span class="fw-bold text-primary fs-4">{{ cart.total }}</span>
</div>
<!-- Checkout Button -->
<div class="d-grid mb-3">
{% if user_json %}
<a href="/checkout" class="btn btn-primary btn-lg">
<i class="bi bi-credit-card me-2"></i>Proceed to Checkout
</a>
{% else %}
<button class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#guestCheckoutModal">
<i class="bi bi-credit-card me-2"></i>Proceed to Checkout
</button>
{% endif %}
</div>
<!-- Additional Actions -->
<div class="d-grid gap-2">
<button class="btn btn-outline-secondary btn-sm" data-action="save-for-later">
<i class="bi bi-bookmark me-1"></i>Save for Later
</button>
<button class="btn btn-outline-info btn-sm" data-action="share-cart">
<i class="bi bi-share me-1"></i>Share Cart
</button>
</div>
<!-- Security Notice -->
<div class="mt-3 p-3 security-notice rounded">
<div class="d-flex align-items-center">
<i class="bi bi-shield-check text-success me-2 fs-5"></i>
<div>
<small class="fw-bold">Secure Checkout</small>
<div class="small text-muted">256-bit SSL encryption</div>
</div>
</div>
</div>
</div>
</div>
<!-- Recommended Products -->
<div class="card shadow-sm mt-4">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-lightbulb me-2"></i>You might also like</h6>
</div>
<div class="card-body">
<div class="row g-2">
<div class="col-12">
<div class="d-flex align-items-center p-3 border rounded hover-shadow">
<i class="bi bi-cpu text-primary me-3 fs-4"></i>
<div class="flex-grow-1">
<div class="fw-bold small">High-Performance Compute</div>
<div class="text-muted" style="font-size: 0.75rem;">Starting at $5/month</div>
</div>
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-plus"></i>
</button>
</div>
</div>
<div class="col-12">
<div class="d-flex align-items-center p-3 border rounded hover-shadow">
<i class="bi bi-hdd-rack text-success me-3 fs-4"></i>
<div class="flex-grow-1">
<div class="fw-bold small">Storage Solutions</div>
<div class="text-muted" style="font-size: 0.75rem;">Starting at $2.50/month</div>
</div>
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-plus"></i>
</button>
</div>
</div>
<div class="col-12">
<div class="d-flex align-items-center p-3 border rounded hover-shadow">
<i class="bi bi-globe text-info me-3 fs-4"></i>
<div class="flex-grow-1">
<div class="fw-bold small">Gateway Services</div>
<div class="text-muted" style="font-size: 0.75rem;">Starting at $1.50/month</div>
</div>
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-plus"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Clear Cart Confirmation Modal -->
<div class="modal fade" id="clearCartModal" tabindex="-1" aria-labelledby="clearCartModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="clearCartModalLabel">
<i class="bi bi-exclamation-triangle me-2"></i>Clear Entire Cart
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center mb-3">
<i class="bi bi-cart-x text-danger me-3" style="font-size: 2rem;"></i>
<div>
<p class="mb-1 fw-bold">Are you sure you want to clear your entire cart?</p>
<p class="mb-0 text-muted small">This action will remove all items from your cart and cannot be undone.</p>
</div>
</div>
<div class="alert alert-warning d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<small>All {{ cart.item_count }} items will be permanently removed from your cart.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i>Cancel
</button>
<button type="button" class="btn btn-danger" id="confirmClearCartBtn">
<i class="bi bi-trash me-1"></i>Clear Cart
</button>
</div>
</div>
</div>
</div>
<!-- Remove Item Confirmation Modal -->
<div class="modal fade" id="removeItemModal" tabindex="-1" aria-labelledby="removeItemModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="removeItemModalLabel">
<i class="bi bi-exclamation-triangle me-2"></i>Remove Item
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center mb-3">
<i class="bi bi-trash text-warning me-3" style="font-size: 2rem;"></i>
<div>
<p class="mb-1 fw-bold">Remove this item from your cart?</p>
<p class="mb-0 text-muted small">This action cannot be undone.</p>
</div>
</div>
<div class="alert alert-info d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<small>You can always add this item back later from the marketplace.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i>Cancel
</button>
<button type="button" class="btn btn-warning" id="confirmRemoveItemBtn">
<i class="bi bi-trash me-1"></i>Remove Item
</button>
</div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div id="loadingOverlay" class="position-fixed top-0 start-0 w-100 h-100 d-none loading-overlay">
<div class="d-flex justify-content-center align-items-center h-100">
<div class="spinner-border text-light" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script type="application/json" id="cart-hydration">
{
"item_count": {{ cart.item_count }},
"redirect_login_url": "/login?checkout=true",
"redirect_register_url": "/register?checkout=true",
"redirect_after_auth": "/cart"
}
</script>
<script src="/static/js/cart.js"></script>
<!-- Guest Checkout Modal -->
<div class="modal fade" id="guestCheckoutModal" tabindex="-1" aria-labelledby="guestCheckoutModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="guestCheckoutModalLabel">
<i class="bi bi-person-check me-2"></i>Complete Your Purchase
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center mb-4">
<i class="bi bi-cart-check text-primary" style="font-size: 3rem;"></i>
<h4 class="mt-3 mb-2">Almost there!</h4>
<p class="text-muted">You're just one step away from completing your purchase.</p>
</div>
<div class="alert alert-info d-flex align-items-center mb-4">
<i class="bi bi-shield-check me-2"></i>
<div>
<strong>Your cart is safe!</strong><br>
<small>All items in your cart will be automatically saved when you log in or create an account.</small>
</div>
</div>
<div class="row g-3">
<div class="col-12">
<h6 class="mb-3">Choose an option to continue:</h6>
</div>
<div class="col-md-6">
<div class="card h-100 border-primary">
<div class="card-body text-center">
<i class="bi bi-person-fill text-primary mb-2" style="font-size: 2rem;"></i>
<h6>Already have an account?</h6>
<p class="text-muted small mb-3">Sign in to access your saved information and complete checkout quickly.</p>
<button type="button" class="btn btn-primary w-100" id="guestLoginBtn">
<i class="bi bi-box-arrow-in-right me-2"></i>Log In
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 border-success">
<div class="card-body text-center">
<i class="bi bi-person-plus-fill text-success mb-2" style="font-size: 2rem;"></i>
<h6>New to ThreeFold?</h6>
<p class="text-muted small mb-3">Create a free account to manage your orders and access exclusive features.</p>
<button type="button" class="btn btn-success w-100" id="guestRegisterBtn">
<i class="bi bi-person-plus me-2"></i>Create Account
</button>
</div>
</div>
</div>
</div>
<div class="mt-4 p-3 bg-light rounded">
<div class="row align-items-center">
<div class="col-auto">
<i class="bi bi-lightning-charge text-warning" style="font-size: 1.5rem;"></i>
</div>
<div class="col">
<h6 class="mb-1">Quick & Secure</h6>
<small class="text-muted">Registration takes less than 30 seconds. Your payment information is protected with enterprise-grade security.</small>
</div>
</div>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
<i class="bi bi-arrow-left me-2"></i>Continue Shopping
</button>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,18 @@
{% extends "marketplace/layout.html" %}
{% block title %}Shopping Cart - Project Mycelium{% endblock %}
{% block marketplace_content %}
<div class="container-fluid py-4">
<h1>Shopping Cart</h1>
<p>Cart page is working!</p>
{% if cart %}
<p>Cart data is available</p>
<p>Items: {{ cart.item_count }}</p>
<p>Currency: {{ cart.currency }}</p>
{% else %}
<p>No cart data available</p>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,573 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shopping Cart - Project Mycelium</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.navbar-brand img {
height: 30px;
}
.cart-item {
transition: all 0.3s ease;
background: white;
}
.cart-item:hover {
background-color: #f8f9fa;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.product-icon {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 12px;
border: 2px solid #dee2e6;
}
.quantity-controls {
display: flex;
align-items: center;
gap: 0.5rem;
}
.quantity-controls button {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
}
.cart-summary {
position: sticky;
top: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.security-notice {
background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%);
border: 1px solid #b6d4da;
border-radius: 8px;
}
.empty-cart-icon {
font-size: 4rem;
color: #6c757d;
}
.btn-primary {
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #0b5ed7 0%, #0a58ca 100%);
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.badge {
border-radius: 6px;
}
.hover-shadow:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
transform: translateY(-1px);
}
.add-recommended-btn {
transition: all 0.2s ease;
min-width: 60px;
}
.add-recommended-btn:hover {
transform: scale(1.05);
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item + .breadcrumb-item::before {
content: "";
font-weight: bold;
}
.hover-shadow:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
transform: translateY(-1px);
transition: all 0.2s ease;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo_dark.png" alt="ThreeFold Logo" class="me-2">
<span>Project Mycelium</span>
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/marketplace">
<i class="bi bi-shop me-1"></i>Marketplace
</a>
<a class="nav-link active" href="/cart">
<i class="bi bi-cart3 me-1"></i>Cart
</a>
<a class="nav-link" href="/orders" id="myOrdersLink" style="display: none;">
<i class="bi bi-list-ul me-1"></i>My Orders
</a>
</div>
</div>
</nav>
<div class="container py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item"><a href="/marketplace" class="text-decoration-none">Marketplace</a></li>
<li class="breadcrumb-item active" aria-current="page">Shopping Cart</li>
</ol>
</nav>
<!-- Page Header -->
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-4 border-bottom">
<h1 class="h2 mb-0">
<i class="bi bi-cart3 me-2 text-primary"></i>Shopping Cart
</h1>
<div class="btn-toolbar">
<a href="/marketplace" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Continue Shopping
</a>
</div>
</div>
{% if cart.is_empty %}
<!-- Empty Cart State -->
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body py-5">
<i class="bi bi-cart-x empty-cart-icon mb-4"></i>
<h3 class="text-muted mb-3">Your cart is empty</h3>
<p class="text-muted mb-4">
Discover amazing ThreeFold resources and services. From compute power to storage solutions,
find everything you need to build on the decentralized internet.
</p>
<a href="/marketplace" class="btn btn-primary btn-lg">
<i class="bi bi-shop me-2"></i>Explore Marketplace
</a>
</div>
</div>
</div>
</div>
{% else %}
<!-- Cart with Items -->
<div class="row">
<!-- Cart Items -->
<div class="col-lg-8">
<div class="card mb-4">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-bag me-2"></i>Cart Items
<span class="badge bg-primary">{{ cart.item_count }}</span>
</h5>
<button class="btn btn-sm btn-outline-danger" id="clearCartBtn">
<i class="bi bi-trash me-1"></i>Clear All
</button>
</div>
<div class="card-body p-0">
{% for item in cart.items %}
<div class="cart-item p-4 border-bottom" data-product-id="{{ item.product_id }}">
<div class="row align-items-center">
<!-- Product Info -->
<div class="col-md-6">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="product-icon">
{% if item.product_category == "compute" %}
<i class="bi bi-cpu fs-4 text-primary"></i>
{% elif item.product_category == "hardware" %}
<i class="bi bi-hdd-rack fs-4 text-success"></i>
{% elif item.product_category == "gateways" %}
<i class="bi bi-globe fs-4 text-info"></i>
{% elif item.product_category == "applications" %}
<i class="bi bi-app fs-4 text-warning"></i>
{% elif item.product_category == "services" %}
<i class="bi bi-person-workspace fs-4 text-secondary"></i>
{% else %}
<i class="bi bi-box fs-4 text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-1 fw-bold">{{ item.product_name }}</h6>
<p class="text-muted mb-2 small">
<i class="bi bi-building me-1"></i>{{ item.provider_name }}
</p>
<span class="badge bg-light text-dark border">
{{ item.product_category|title }}
</span>
{% if item.specifications %}
<div class="mt-2">
<small class="text-muted fw-bold">Configuration:</small>
<div class="mt-1">
{% for key, value in item.specifications %}
<span class="badge bg-secondary me-1 small">{{ key }}: {{ value }}</span>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Quantity Controls -->
<div class="col-md-3">
<div class="quantity-controls justify-content-center">
<button class="btn btn-sm btn-outline-secondary" data-product-id="{{ item.product_id }}" data-action="decrease">
<i class="bi bi-dash"></i>
</button>
<span class="mx-3 fw-bold fs-5">{{ item.quantity }}</span>
<button class="btn btn-sm btn-outline-secondary" data-product-id="{{ item.product_id }}" data-action="increase">
<i class="bi bi-plus"></i>
</button>
</div>
<div class="text-center mt-2">
<small class="text-muted">Quantity</small>
</div>
</div>
<!-- Price and Actions -->
<div class="col-md-3">
<div class="text-end">
<div class="mb-2">
<small class="text-muted">Unit: {{ item.unit_price }}</small>
</div>
<div class="fw-bold text-primary mb-3 fs-5">{{ item.total_price }}</div>
<button class="btn btn-sm btn-outline-danger" data-product-id="{{ item.product_id }}" data-action="remove">
<i class="bi bi-trash me-1"></i>Remove
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Cart Summary -->
<div class="col-lg-4">
<div class="cart-summary p-4">
<h5 class="mb-3">
<i class="bi bi-receipt me-2 text-primary"></i>Order Summary
</h5>
<!-- Currency Selector -->
<div class="mb-3">
<label class="form-label small text-muted fw-bold">
<i class="bi bi-currency-exchange me-1"></i>Display Currency
</label>
<select class="form-select form-select-sm" id="currencySelector">
{% for currency in currencies %}
<option value="{{ currency.code }}" {% if currency.code == user_currency %}selected{% endif %}>
{{ currency.symbol }} {{ currency.name }}
</option>
{% endfor %}
</select>
</div>
<hr>
<!-- Price Breakdown -->
<div class="d-flex justify-content-between mb-2">
<span><i class="bi bi-bag me-1"></i>Subtotal ({{ cart.item_count }} items)</span>
<span class="fw-bold">{{ cart.subtotal }}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted"><i class="bi bi-calculator me-1"></i>Platform fees</span>
<span class="text-muted">Calculated at checkout</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted"><i class="bi bi-truck me-1"></i>Deployment</span>
<span class="text-success fw-bold">Free</span>
</div>
<hr>
<div class="d-flex justify-content-between mb-4">
<span class="fw-bold fs-5">Total</span>
<span class="fw-bold text-primary fs-4">{{ cart.total }}</span>
</div>
<!-- Guest User Notice -->
{% if not user_json %}
<div class="alert alert-info mb-3">
<div class="d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<div>
<strong>Almost ready to checkout!</strong><br>
<small>You'll need to log in or create an account to complete your purchase. Don't worry - your cart items will be saved!</small>
</div>
</div>
</div>
{% endif %}
<!-- Checkout Button -->
<div class="d-grid mb-3">
{% if user_json %}
<a href="/checkout" class="btn btn-primary btn-lg">
<i class="bi bi-credit-card me-2"></i>Proceed to Checkout
</a>
{% else %}
<button class="btn btn-primary btn-lg" id="checkoutBtn">
<i class="bi bi-credit-card me-2"></i>Proceed to Checkout
</button>
{% endif %}
</div>
<!-- Security Notice -->
<div class="security-notice p-3">
<div class="d-flex align-items-center">
<i class="bi bi-shield-check text-success me-2 fs-5"></i>
<div>
<small class="fw-bold">Secure Checkout</small>
<div class="small text-muted">256-bit SSL encryption</div>
</div>
</div>
</div>
</div>
<!-- Recommended Products -->
<div class="card mt-4">
<div class="card-header bg-light">
<h6 class="mb-0">
<i class="bi bi-stars me-2"></i>Recommended for you
</h6>
</div>
<div class="card-body">
{% for product_data in recommended_products %}
<div class="d-flex align-items-center p-3 border rounded hover-shadow mb-3" style="transition: all 0.2s ease;">
<i class="bi bi-cpu text-primary me-3 fs-4"></i>
<div class="flex-grow-1">
<div class="fw-bold">{{ product_data.product.name }}</div>
<small class="text-muted">{{ product_data.product.description }}</small>
<div class="text-primary fw-bold mt-1">{{ product_data.formatted_price }}</div>
</div>
<button class="btn btn-sm btn-primary add-recommended-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-product-price="{{ product_data.price.display_amount }}"
data-product-category="{{ product_data.product.category_id }}">
<i class="bi bi-plus me-1"></i>Add
</button>
</div>
{% endfor %}
<div class="text-center mt-3">
<a href="/marketplace" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-shop me-1"></i>Browse All Products
</a>
</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Clear Cart Confirmation Modal -->
<div class="modal fade" id="clearCartModal" tabindex="-1" aria-labelledby="clearCartModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="clearCartModalLabel">
<i class="bi bi-exclamation-triangle me-2"></i>Clear Entire Cart
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center mb-3">
<i class="bi bi-cart-x text-danger me-3" style="font-size: 2rem;"></i>
<div>
<p class="mb-1 fw-bold">Are you sure you want to clear your entire cart?</p>
<p class="mb-0 text-muted small">This action will remove all items from your cart and cannot be undone.</p>
</div>
</div>
<div class="alert alert-warning d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<small>All {{ cart.item_count }} items will be permanently removed from your cart.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i>Cancel
</button>
<button type="button" class="btn btn-danger" id="confirmClearCartBtn">
<i class="bi bi-trash me-1"></i>Clear Cart
</button>
</div>
</div>
</div>
</div>
<!-- Remove Item Confirmation Modal -->
<div class="modal fade" id="removeItemModal" tabindex="-1" aria-labelledby="removeItemModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="removeItemModalLabel">
<i class="bi bi-exclamation-triangle me-2"></i>Remove Item
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center mb-3">
<i class="bi bi-trash text-warning me-3" style="font-size: 2rem;"></i>
<div>
<p class="mb-1 fw-bold">Remove this item from your cart?</p>
<p class="mb-0 text-muted small">This action cannot be undone.</p>
</div>
</div>
<div class="alert alert-info d-flex align-items-center">
<i class="bi bi-info-circle me-2"></i>
<small>You can always add this item back later from the marketplace.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i>Cancel
</button>
<button type="button" class="btn btn-warning" id="confirmRemoveItemBtn">
<i class="bi bi-trash me-1"></i>Remove Item
</button>
</div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div id="loadingOverlay" class="position-fixed top-0 start-0 w-100 h-100 d-none" style="background: rgba(0,0,0,0.5); z-index: 9999;">
<div class="d-flex justify-content-center align-items-center h-100">
<div class="spinner-border text-light" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script type="application/json" id="cart-hydration">
{
"item_count": {{ cart.item_count }},
"redirect_login_url": "/login?checkout=true",
"redirect_register_url": "/register?checkout=true",
"redirect_after_auth": "/cart"
}
</script>
<script src="/static/js/cart.js"></script>
<!-- Guest Checkout Modal -->
<div class="modal fade" id="guestCheckoutModal" tabindex="-1" aria-labelledby="guestCheckoutModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="guestCheckoutModalLabel">
<i class="bi bi-person-check me-2"></i>Complete Your Purchase
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center mb-4">
<i class="bi bi-cart-check text-primary" style="font-size: 3rem;"></i>
<h4 class="mt-3 mb-2">Almost there!</h4>
<p class="text-muted">You're just one step away from completing your purchase.</p>
</div>
<div class="alert alert-info d-flex align-items-center mb-4">
<i class="bi bi-shield-check me-2"></i>
<div>
<strong>Your cart is safe!</strong><br>
<small>All items in your cart will be automatically saved when you log in or create an account.</small>
</div>
</div>
<div class="row g-3">
<div class="col-12">
<h6 class="mb-3">Choose an option to continue:</h6>
</div>
<div class="col-md-6">
<div class="card h-100 border-primary">
<div class="card-body text-center">
<i class="bi bi-person-fill text-primary mb-2" style="font-size: 2rem;"></i>
<h6>Already have an account?</h6>
<p class="text-muted small mb-3">Sign in to access your saved information and complete checkout quickly.</p>
<button type="button" class="btn btn-primary w-100" id="guestLoginBtn">
<i class="bi bi-box-arrow-in-right me-2"></i>Log In
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 border-success">
<div class="card-body text-center">
<i class="bi bi-person-plus-fill text-success mb-2" style="font-size: 2rem;"></i>
<h6>New to ThreeFold?</h6>
<p class="text-muted small mb-3">Create a free account to manage your orders and access exclusive features.</p>
<button type="button" class="btn btn-success w-100" id="guestRegisterBtn">
<i class="bi bi-person-plus me-2"></i>Create Account
</button>
</div>
</div>
</div>
</div>
<div class="mt-4 p-3 bg-light rounded">
<div class="row align-items-center">
<div class="col-auto">
<i class="bi bi-lightning-charge text-warning" style="font-size: 1.5rem;"></i>
</div>
<div class="col">
<h6 class="mb-1">Quick & Secure</h6>
<small class="text-muted">Registration takes less than 30 seconds. Your payment information is protected with enterprise-grade security.</small>
</div>
</div>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
<i class="bi bi-arrow-left me-2"></i>Continue Shopping
</button>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,342 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Checkout - Project Mycelium</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.navbar-brand img {
height: 30px;
}
.checkout-step {
position: relative;
padding: 1rem;
border-radius: 8px;
transition: all 0.3s ease;
}
.checkout-step.active {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border: 2px solid #2196f3;
}
.checkout-step.completed {
background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%);
border: 2px solid #4caf50;
}
.step-number {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 1rem;
}
.step-number.active {
background: #2196f3;
color: white;
}
.step-number.completed {
background: #4caf50;
color: white;
}
.step-number.pending {
background: #e0e0e0;
color: #757575;
}
.payment-method {
border: 2px solid #e0e0e0;
border-radius: 8px;
padding: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.payment-method:hover {
border-color: #2196f3;
background-color: #f5f5f5;
}
.payment-method.selected {
border-color: #2196f3;
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
}
.order-summary {
position: sticky;
top: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.security-badge {
background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%);
border: 1px solid #4caf50;
border-radius: 8px;
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn-primary {
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #0b5ed7 0%, #0a58ca 100%);
}
.form-control:focus {
border-color: #2196f3;
box-shadow: 0 0 0 0.2rem rgba(33, 150, 243, 0.25);
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item + .breadcrumb-item::before {
content: "";
font-weight: bold;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo_dark.png" alt="ThreeFold Logo" class="me-2">
<span>Project Mycelium</span>
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/marketplace">
<i class="bi bi-shop me-1"></i>Marketplace
</a>
<a class="nav-link" href="/cart">
<i class="bi bi-cart3 me-1"></i>Cart
</a>
<a class="nav-link active" href="/checkout">
<i class="bi bi-credit-card me-1"></i>Checkout
</a>
</div>
</div>
</nav>
<div class="container py-4">
<!-- Page Header -->
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-4 border-bottom">
<h1 class="h2 mb-0">
<i class="bi bi-credit-card me-2 text-primary"></i>Secure Checkout
</h1>
<div class="btn-toolbar">
<a href="/cart" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Back to Cart
</a>
</div>
</div>
<!-- Checkout Steps -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between">
<div class="checkout-step active flex-fill me-2">
<div class="d-flex align-items-center">
<div class="step-number active">1</div>
<div>
<div class="fw-bold">Review Order</div>
<small class="text-muted">Verify your items</small>
</div>
</div>
</div>
<div class="checkout-step pending flex-fill me-2">
<div class="d-flex align-items-center">
<div class="step-number pending">2</div>
<div>
<div class="fw-bold">Payment</div>
<small class="text-muted">Choose payment method</small>
</div>
</div>
</div>
<div class="checkout-step pending flex-fill">
<div class="d-flex align-items-center">
<div class="step-number pending">3</div>
<div>
<div class="fw-bold">Confirmation</div>
<small class="text-muted">Order complete</small>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Checkout Form -->
<div class="col-lg-8">
<!-- Step 1: Order Review -->
<div id="step1" class="checkout-section">
<div class="card mb-4">
<div class="card-header bg-white">
<h5 class="mb-0">
<i class="bi bi-bag-check me-2"></i>Order Review
</h5>
</div>
<div class="card-body">
{% if cart_details and cart_details.items %}
{% for item in cart_details.items %}
<div class="d-flex align-items-center p-3 border-bottom">
<div class="me-3">
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
{% if item.product.category_id == "compute" %}
<i class="bi bi-cpu text-primary"></i>
{% elif item.product.category_id == "hardware" %}
<i class="bi bi-hdd-rack text-success"></i>
{% elif item.product.category_id == "gateways" %}
<i class="bi bi-globe text-info"></i>
{% elif item.product.category_id == "applications" %}
<i class="bi bi-app text-warning"></i>
{% elif item.product.category_id == "services" %}
<i class="bi bi-person-workspace text-secondary"></i>
{% else %}
<i class="bi bi-box text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-1">{{ item.product.name }}</h6>
<small class="text-muted">{{ item.product.provider_name }}</small>
<div class="mt-1">
<span class="badge bg-light text-dark">Qty: {{ item.cart_item.quantity }}</span>
</div>
</div>
<div class="text-end">
<div class="fw-bold text-primary">{{ item.unit_price.formatted_display }}</div>
<small class="text-muted">per unit</small>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-4">
<i class="bi bi-cart-x fs-1 text-muted mb-3"></i>
<h5 class="text-muted">No items in cart</h5>
<a href="/marketplace" class="btn btn-primary">Browse Marketplace</a>
</div>
{% endif %}
</div>
</div>
<div class="d-flex justify-content-end">
<button class="btn btn-primary btn-lg" id="complete-order-btn">
Complete Order <i class="bi bi-lock ms-1"></i>
</button>
</div>
</div>
</div>
<!-- Order Summary -->
<div class="col-lg-4">
<div class="order-summary p-4">
<h5 class="mb-3">
<i class="bi bi-receipt me-2 text-primary"></i>Order Summary
</h5>
{% if cart_details %}
<div class="d-flex justify-content-between mb-2">
<span>Subtotal ({{ cart_details.item_count }} items)</span>
<span class="fw-bold" id="subtotal-display">{{ cart_details.subtotal }} {{ cart_details.currency }}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Platform fee</span>
<span class="text-muted">Free</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Deployment</span>
<span class="text-success fw-bold">Free</span>
</div>
<hr>
<div class="d-flex justify-content-between mb-4">
<span class="fw-bold fs-5">Total</span>
<span class="fw-bold text-primary fs-4" id="total-display">{{ cart_details.total }} {{ cart_details.currency }}</span>
</div>
{% endif %}
<!-- Security Badges -->
<div class="security-badge p-3 mb-3">
<div class="d-flex align-items-center mb-2">
<i class="bi bi-shield-check text-success me-2 fs-5"></i>
<div>
<div class="fw-bold small">Secure Checkout</div>
<div class="small text-muted">256-bit SSL encryption</div>
</div>
</div>
<div class="d-flex align-items-center">
<i class="bi bi-lock text-success me-2 fs-5"></i>
<div>
<div class="fw-bold small">Privacy Protected</div>
<div class="small text-muted">Your data is safe with us</div>
</div>
</div>
</div>
<!-- Support -->
<div class="text-center">
<small class="text-muted">
Need help? <a href="https://threefoldfaq.crisp.help/en/" class="text-decoration-none" target="_blank">Contact Support</a>
</small>
</div>
</div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div id="loadingOverlay" class="position-fixed top-0 start-0 w-100 h-100 d-none" style="background: rgba(0,0,0,0.5); z-index: 9999;">
<div class="d-flex justify-content-center align-items-center h-100">
<div class="spinner-border text-light" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Processing...</span>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<!-- Modal + Errors utilities (must load before checkout.js) -->
<script src="/static/js/modal-system.js"></script>
<script src="/static/js/utils/errors.js"></script>
<!-- JSON hydration for checkout (CSP-safe) -->
<script type="application/json" id="checkout-hydration">
{
"user_currency": {{ user_currency | default(value='USD') | json_encode() | safe }},
"cart_details": {% if cart_details is defined %}{{ cart_details | json_encode() | safe }}{% else %}null{% endif %}
}
</script>
<!-- External JS (CSP-compliant) -->
<script src="/static/js/base.js"></script>
<script src="/static/js/checkout.js"></script>
</body>
</html>

View File

@@ -0,0 +1,445 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Compute Resources{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>Compute Resources (Slices)</h1>
<p class="lead">Find and acquire the compute resources you need for your workloads.</p>
<!-- Enhanced Filter Section -->
<div class="filter-section p-4 mb-4">
<h5 class="mb-3">Filter Resources</h5>
<form id="filterForm" method="GET">
<div class="enhanced-filters">
<div>
<label class="form-label">Location</label>
<select name="location" class="form-select">
<option value="">All Locations</option>
<option value="Belgium">Belgium</option>
<option value="Austria">Austria</option>
<option value="Switzerland">Switzerland</option>
<option value="Netherlands">Netherlands</option>
<option value="Germany">Germany</option>
<option value="France">France</option>
<option value="UK">UK</option>
<option value="Canada">Canada</option>
<option value="USA">USA</option>
<option value="Japan">Japan</option>
</select>
</div>
<div>
<label class="form-label">Min vCores</label>
<select name="min_cores" class="form-select">
<option value="">Any</option>
<option value="1">1 Core+</option>
<option value="2">2 Cores+</option>
<option value="4">4 Cores+</option>
<option value="8">8 Cores+</option>
<option value="16">16 Cores+</option>
</select>
</div>
<div>
<label class="form-label">Min RAM (GB)</label>
<select name="min_memory" class="form-select">
<option value="">Any</option>
<option value="4">4 GB+</option>
<option value="8">8 GB+</option>
<option value="16">16 GB+</option>
<option value="32">32 GB+</option>
</select>
</div>
<div>
<label class="form-label">Min Storage (GB)</label>
<select name="min_storage" class="form-select">
<option value="">Any</option>
<option value="200">200 GB+</option>
<option value="500">500 GB+</option>
<option value="1000">1 TB+</option>
<option value="2000">2 TB+</option>
</select>
</div>
<div>
<label class="form-label">Min Uptime (%)</label>
<select name="min_uptime" class="form-select">
<option value="">Any</option>
<option value="95">95%+</option>
<option value="98">98%+</option>
<option value="99">99%+</option>
<option value="99.9">99.9%+</option>
</select>
</div>
<div>
<label class="form-label">Min Bandwidth (Mbps)</label>
<select name="min_bandwidth" class="form-select">
<option value="">Any</option>
<option value="100">100 Mbps+</option>
<option value="500">500 Mbps+</option>
<option value="1000">1 Gbps+</option>
</select>
</div>
<div>
<label class="form-label">Price Range ($)</label>
<div class="d-flex gap-2">
<input type="number" name="min_price" class="form-control" placeholder="Min" style="width: 80px;">
<input type="number" name="max_price" class="form-control" placeholder="Max" style="width: 80px;">
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Apply Filters</button>
<a href="/marketplace/compute" class="btn btn-outline-secondary ms-2">Clear</a>
</div>
</div>
</form>
</div>
<!-- Table Section -->
{% if compute_products and compute_products | length > 0 %}
<div class="compute-table">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th style="width: 15%;">Type</th>
<th style="width: 12%;">Provider</th>
<th style="width: 10%;">Location</th>
<th style="width: 8%;">vCores</th>
<th style="width: 8%;">RAM</th>
<th style="width: 10%;">Storage (SSD)</th>
<th style="width: 10%;">Uptime SLA</th>
<th style="width: 10%;">Bandwidth SLA</th>
<th style="width: 8%;">Price/Hour</th>
<th style="width: 9%;">Actions</th>
</tr>
</thead>
<tbody>
{% for product_data in compute_products %}
<tr>
<td>
<div class="slice-name">{{ product_data.product.name }}</div>
<div class="slice-info">
<small class="text-muted d-block">
{% if product_data.product.attributes.cpu_cores %}
{{ product_data.product.attributes.cpu_cores.value }}x Base Unit
{% else %}
Compute slice
{% endif %}
</small>
</div>
</td>
<td>
<div class="provider-name">
{% if product_data.product.attributes.farmer_email %}
{{ product_data.product.attributes.farmer_email.value | truncate(length=15) }}
{% else %}
{% if product_data.product.provider %}{{ product_data.product.provider }}{% else %}Unknown{% endif %}
{% endif %}
</div>
</td>
<td>
{% if product_data.product.metadata.location %}
<span class="location-badge node-location"
data-location="{{ product_data.product.metadata.location }}">
{{ product_data.product.metadata.location }}
</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if product_data.product.attributes.cpu_cores %}
<span class="spec-value">{{ product_data.product.attributes.cpu_cores.value }}</span>
<span class="spec-unit">cores</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if product_data.product.attributes.memory_gb %}
<span class="spec-value">{{ product_data.product.attributes.memory_gb.value }}</span>
<span class="spec-unit">GB</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if product_data.product.attributes.storage_gb %}
<span class="spec-value">{{ product_data.product.attributes.storage_gb.value }}</span>
<span class="spec-unit">GB</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if product_data.product.attributes.uptime_percentage %}
<div class="sla-indicator sla-good">
<i class="bi bi-check-circle-fill"></i>
<span>{{ product_data.product.attributes.uptime_percentage.value | format_decimal(precision=1) }}%</span>
</div>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if product_data.product.attributes.bandwidth_mbps %}
<div class="sla-indicator sla-good">
<i class="bi bi-speedometer2"></i>
<span>{{ product_data.product.attributes.bandwidth_mbps.value }} Mbps</span>
</div>
{% else %}
<div class="sla-indicator sla-good">
<i class="bi bi-speedometer2"></i>
<span>1000 Mbps</span>
</div>
{% endif %}
</td>
<td>
<div class="price-display">{{ product_data.formatted_price }}</div>
</td>
<td>
<div class="action-buttons">
<button class="btn btn-success btn-sm-custom buy-now-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}"
data-category="compute">
<i class="bi bi-lightning-charge"></i> Buy Now
</button>
<button class="btn btn-primary btn-sm-custom add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus"></i> Add to Cart
</button>
<a class="btn btn-outline-secondary btn-sm-custom" href="/products/{{ product_data.product.id }}">
<i class="bi bi-eye"></i> View
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-inbox display-1 text-muted"></i>
<h4 class="mt-3">No Compute Resources Available</h4>
<p class="text-muted">Check back later for new compute resources.</p>
</div>
{% endif %}
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<nav aria-label="Resource pages" class="mt-4">
<ul class="pagination justify-content-center">
<!-- Previous Page -->
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
{% if pagination.has_previous %}
<a class="page-link" href="?page={{ pagination.previous_page }}">Previous</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Previous</span>
{% endif %}
</li>
<!-- Page 1 -->
<li class="page-item {% if pagination.current_page == 0 %}active{% endif %}">
{% if pagination.current_page == 0 %}
<span class="page-link">1</span>
{% else %}
<a class="page-link" href="?page=0">1</a>
{% endif %}
</li>
<!-- Page 2 (if exists) -->
{% if pagination.total_pages > 1 %}
<li class="page-item {% if pagination.current_page == 1 %}active{% endif %}">
{% if pagination.current_page == 1 %}
<span class="page-link">2</span>
{% else %}
<a class="page-link" href="?page=1">2</a>
{% endif %}
</li>
{% endif %}
<!-- Page 3 (if exists) -->
{% if pagination.total_pages > 2 %}
<li class="page-item {% if pagination.current_page == 2 %}active{% endif %}">
{% if pagination.current_page == 2 %}
<span class="page-link">3</span>
{% else %}
<a class="page-link" href="?page=2">3</a>
{% endif %}
</li>
{% endif %}
<!-- Page 4 (if exists) -->
{% if pagination.total_pages > 3 %}
<li class="page-item {% if pagination.current_page == 3 %}active{% endif %}">
{% if pagination.current_page == 3 %}
<span class="page-link">4</span>
{% else %}
<a class="page-link" href="?page=3">4</a>
{% endif %}
</li>
{% endif %}
<!-- Page 5 (if exists) -->
{% if pagination.total_pages > 4 %}
<li class="page-item {% if pagination.current_page == 4 %}active{% endif %}">
{% if pagination.current_page == 4 %}
<span class="page-link">5</span>
{% else %}
<a class="page-link" href="?page=4">5</a>
{% endif %}
</li>
{% endif %}
<!-- Next Page -->
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
{% if pagination.has_next %}
<a class="page-link" href="?page={{ pagination.next_page }}">Next</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Next</span>
{% endif %}
</li>
</ul>
</nav>
<!-- Results Info -->
<div class="text-center text-muted mt-2">
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
({{ pagination.total_count }} total compute resources)
</div>
{% endif %}
</div>
<style>
.compute-table {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
.compute-table th {
background-color: #f8f9fa;
border-bottom: 2px solid #dee2e6;
font-weight: 600;
color: #495057;
padding: 12px 8px;
font-size: 0.9rem;
}
.compute-table td {
padding: 12px 8px;
vertical-align: middle;
border-bottom: 1px solid #f1f3f4;
}
.compute-table tbody tr:hover {
background-color: #f8f9fa;
}
.provider-name {
font-weight: 600;
color: #0d6efd;
font-size: 0.95rem;
}
.slice-info {
margin-top: 2px;
}
.slice-name {
font-weight: 500;
color: #212529;
font-size: 0.9rem;
}
.spec-value {
font-weight: 500;
color: #212529;
}
.spec-unit {
color: #6c757d;
font-size: 0.85rem;
}
.location-badge {
background-color: #e3f2fd;
color: #1976d2;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 500;
}
.sla-indicator {
display: flex;
align-items: center;
gap: 4px;
}
.sla-good { color: #28a745; }
.sla-medium { color: #ffc107; }
.sla-low { color: #dc3545; }
.price-display {
font-weight: 600;
color: #0d6efd;
font-size: 1.1rem;
}
.action-buttons {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.btn-sm-custom {
padding: 4px 8px;
font-size: 0.8rem;
border-radius: 4px;
}
@media (max-width: 768px) {
.compute-table {
font-size: 0.85rem;
}
.compute-table th,
.compute-table td {
padding: 8px 4px;
}
.action-buttons {
flex-direction: column;
}
}
.filter-section {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.enhanced-filters {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
align-items: end;
}
</style>
{% endblock %}
{% block scripts %}
<script type="application/json" id="compute-hydration">{}</script>
<script src="/static/js/marketplace-compute.js"></script>
{% endblock %}

View File

@@ -0,0 +1,376 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Overview{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>Project Mycelium Overview</h1>
<p class="lead">Explore the decentralized ecosystem of resources, applications, and services.</p>
<!-- Overview Stats -->
<div class="row mt-4">
<div class="col-md-3">
<div class="card text-white bg-primary mb-3">
<div class="card-body">
<h5 class="card-title">Compute Resources</h5>
<p class="card-text">250+ available slices</p>
<a href="/marketplace/compute" class="text-white">Browse Resources →</a>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-success mb-3">
<div class="card-body">
<h5 class="card-title">3Nodes</h5>
<p class="card-text">120+ certified nodes</p>
<a href="/marketplace/3nodes" class="text-white">Browse 3Nodes →</a>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-info mb-3">
<div class="card-body">
<h5 class="card-title">Gateways</h5>
<p class="card-text">45+ active gateways</p>
<a href="/marketplace/gateways" class="text-white">Browse Gateways →</a>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-warning mb-3">
<div class="card-body">
<h5 class="card-title">Applications</h5>
<p class="card-text">80+ self-healing apps</p>
<a href="/marketplace/applications" class="text-white">Browse Applications →</a>
</div>
</div>
</div>
</div>
<!-- Featured Items -->
<h2 class="mt-5 mb-4">Featured Items</h2>
<div class="row">
{% if featured_products is defined and featured_products | length > 0 %}
{% for item in featured_products %}
<div class="col-md-6 col-lg-4 mb-4">
<div class="marketplace-item">
<div class="d-flex justify-content-between align-items-start mb-2">
{% set cat = 'other' %}
{% if item.product is defined and item.product.category_id is defined %}
{% set cat = item.product.category_id %}
{% endif %}
<span class="badge bg-{% if cat == 'compute' %}primary{% elif cat == 'hardware' %}success{% elif cat == 'gateway' %}info{% elif cat == 'application' %}warning{% else %}secondary{% endif %} badge-category">
{{ cat }}
</span>
{% if item.product is defined and item.product.metadata is defined and (item.product.metadata.featured | default(value=false)) %}
<span class="badge bg-warning">Featured</span>
{% endif %}
</div>
{% set prod_name = 'Unnamed' %}
{% set prod_desc = '' %}
{% set provider_name = '' %}
{% set prod_id = '' %}
{% set category_id = '' %}
{% set base_price = 0 %}
{% set provider_id = '' %}
{% if item.product is defined %}
{% if item.product.name is defined %}{% set prod_name = item.product.name %}{% endif %}
{% if item.product.description is defined %}{% set prod_desc = item.product.description %}{% endif %}
{% if item.product.provider_name is defined %}{% set provider_name = item.product.provider_name %}{% endif %}
{% if item.product.id is defined %}{% set prod_id = item.product.id %}{% endif %}
{% if item.product.category_id is defined %}{% set category_id = item.product.category_id %}{% endif %}
{% if item.product.base_price is defined %}{% set base_price = item.product.base_price %}{% endif %}
{% if item.product.provider_id is defined %}{% set provider_id = item.product.provider_id %}{% endif %}
{% endif %}
<h4>{{ prod_name }}</h4>
<p>{{ prod_desc }}</p>
<!-- Product Specifications -->
{% if item.product is defined and item.product.attributes is defined and item.product.attributes | length > 0 %}
<div class="row mb-3">
{% for attr_name, attr_value in item.product.attributes %}
{% if attr_name == "cpu_cores" %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-cpu me-1"></i>
<small>{{ attr_value.value | default(value='') }} Cores</small>
</div>
</div>
{% elif attr_name == "memory_gb" %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-memory me-1"></i>
<small>{{ attr_value.value | default(value='') }} GB RAM</small>
</div>
</div>
{% elif attr_name == "storage_gb" %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-hdd me-1"></i>
<small>{{ attr_value.value | default(value='') }} GB Storage</small>
</div>
</div>
{% elif attr_name == "location" %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-geo-alt me-1"></i>
<small>{{ attr_value.value | default(value='') }}</small>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-primary fw-bold">{{ item.formatted_price | default(value="") }}</span>
<br><small class="text-muted">by {{ provider_name }}</small>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-success buy-now-btn"
data-product-id="{{ prod_id }}"
data-product-name="{{ prod_name }}"
data-category="{{ category_id }}"
data-unit-price="{{ base_price }}"
data-provider-id="{{ provider_id }}"
data-provider-name="{{ provider_name }}"
title="Buy instantly with your wallet balance">
<i class="bi bi-lightning-fill me-1"></i>Buy Now
</button>
<button class="btn btn-sm btn-outline-primary add-to-cart-btn"
data-product-id="{{ prod_id }}"
data-product-name="{{ prod_name }}"
data-product-price="{{ item.formatted_price | default(value='') }}">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
<a href="/products/{{ prod_id }}" class="btn btn-sm btn-outline-primary">
View Details
</a>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
No featured products available at the moment.
</div>
</div>
{% endif %}
</div>
<!-- Popular Applications -->
<h2 class="mt-5 mb-4">Popular Applications</h2>
<div class="row">
{% if popular_applications is defined and popular_applications | length > 0 %}
{% for item in popular_applications %}
<div class="col-md-6 col-lg-4 mb-4">
<div class="marketplace-item">
<div class="d-flex justify-content-between align-items-start mb-2">
<span class="badge bg-warning badge-category">Application</span>
{% if item.product is defined and item.product.metadata is defined and (item.product.metadata.featured | default(value=false)) %}
<span class="badge bg-warning">Featured</span>
{% endif %}
</div>
{% set prod_name = 'Unnamed' %}
{% set prod_desc = '' %}
{% set provider_name = '' %}
{% set prod_id = '' %}
{% set category_id = '' %}
{% set base_price = 0 %}
{% set provider_id = '' %}
{% set unit_price = 0 %}
{% set currency = '' %}
{% if item.product is defined %}
{% if item.product.name is defined %}{% set prod_name = item.product.name %}{% endif %}
{% if item.product.description is defined %}{% set prod_desc = item.product.description %}{% endif %}
{% if item.product.provider_name is defined %}{% set provider_name = item.product.provider_name %}{% endif %}
{% if item.product.id is defined %}{% set prod_id = item.product.id %}{% endif %}
{% if item.product.category_id is defined %}{% set category_id = item.product.category_id %}{% endif %}
{% if item.product.base_price is defined %}{% set base_price = item.product.base_price %}{% endif %}
{% if item.product.provider_id is defined %}{% set provider_id = item.product.provider_id %}{% endif %}
{% endif %}
{% if item.price is defined %}
{% if item.price.display_amount is defined %}{% set unit_price = item.price.display_amount %}{% endif %}
{% if item.price.display_currency is defined %}{% set currency = item.price.display_currency %}{% endif %}
{% endif %}
<h4>{{ prod_name }}</h4>
<p>{{ prod_desc | truncate(length=100) }}</p>
<!-- Application Specifications -->
{% if item.product is defined and item.product.attributes is defined and item.product.attributes | length > 0 %}
<div class="row mb-3">
{% if item.product.attributes is defined and item.product.attributes.app_type is defined %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-app me-1"></i>
<small>{{ item.product.attributes.app_type.value | default(value='') }}</small>
</div>
</div>
{% endif %}
{% if item.product.attributes is defined and item.product.attributes.deployment_type is defined %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-box me-1"></i>
<small>{{ item.product.attributes.deployment_type.value | default(value='') }}</small>
</div>
</div>
{% endif %}
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-primary fw-bold">{{ item.formatted_price | default(value="") }}</span>
<br><small class="text-muted">by {{ provider_name }}</small>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-success buy-now-btn"
data-product-id="{{ prod_id }}"
data-product-name="{{ prod_name }}"
data-unit-price="{{ unit_price }}"
data-currency="{{ currency }}">
<i class="bi bi-lightning-fill me-1"></i>Buy Now
</button>
<button class="btn btn-sm btn-outline-primary add-to-cart-btn"
data-product-id="{{ prod_id }}"
data-product-name="{{ prod_name }}"
data-category="{{ category_id }}"
data-unit-price="{{ base_price }}"
data-provider-id="{{ provider_id }}"
data-provider-name="{{ provider_name }}"
data-quantity="1"
title="Buy instantly with your wallet balance">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
<a href="/products/{{ prod_id }}" class="btn btn-sm btn-outline-primary">
View Details
</a>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
No popular applications available at the moment.
</div>
</div>
{% endif %}
</div>
<!-- Available Services -->
<h2 class="mt-5 mb-4">Available Services</h2>
<div class="row">
{% if available_services is defined and available_services | length > 0 %}
{% for item in available_services %}
<div class="col-md-6 col-lg-4 mb-4">
<div class="marketplace-item">
<div class="d-flex justify-content-between align-items-start mb-2">
<span class="badge bg-secondary badge-category">Service</span>
{% if item.product is defined and item.product.metadata is defined and (item.product.metadata.featured | default(value=false)) %}
<span class="badge bg-warning">Featured</span>
{% endif %}
</div>
{% set prod_name = 'Unnamed' %}
{% set prod_desc = '' %}
{% set provider_name = '' %}
{% set prod_id = '' %}
{% set category_id = '' %}
{% set base_price = 0 %}
{% set provider_id = '' %}
{% set unit_price = 0 %}
{% set currency = '' %}
{% if item.product is defined %}
{% if item.product.name is defined %}{% set prod_name = item.product.name %}{% endif %}
{% if item.product.description is defined %}{% set prod_desc = item.product.description %}{% endif %}
{% if item.product.provider_name is defined %}{% set provider_name = item.product.provider_name %}{% endif %}
{% if item.product.id is defined %}{% set prod_id = item.product.id %}{% endif %}
{% if item.product.category_id is defined %}{% set category_id = item.product.category_id %}{% endif %}
{% if item.product.base_price is defined %}{% set base_price = item.product.base_price %}{% endif %}
{% if item.product.provider_id is defined %}{% set provider_id = item.product.provider_id %}{% endif %}
{% endif %}
{% if item.price is defined %}
{% if item.price.display_amount is defined %}{% set unit_price = item.price.display_amount %}{% endif %}
{% if item.price.display_currency is defined %}{% set currency = item.price.display_currency %}{% endif %}
{% endif %}
<h4>{{ prod_name }}</h4>
<p>{{ prod_desc | truncate(length=100) }}</p>
<!-- Service Specifications -->
{% if item.product is defined and item.product.attributes is defined and item.product.attributes | length > 0 %}
<div class="row mb-3">
{% if item.product.attributes is defined and item.product.attributes.service_type is defined %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-gear me-1"></i>
<small>{{ item.product.attributes.service_type.value | default(value='') }}</small>
</div>
</div>
{% endif %}
{% if item.product.attributes is defined and item.product.attributes.expertise_level is defined %}
<div class="col-6">
<div class="spec-item">
<i class="bi bi-star me-1"></i>
<small>{{ item.product.attributes.expertise_level.value | default(value='') }}</small>
</div>
</div>
{% endif %}
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-primary fw-bold">{{ item.formatted_price | default(value="") }}</span>
<br><small class="text-muted">by {{ provider_name }}</small>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-success buy-now-btn"
data-product-id="{{ prod_id }}"
data-product-name="{{ prod_name }}"
data-unit-price="{{ unit_price }}"
data-currency="{{ currency }}">
<i class="bi bi-lightning-fill me-1"></i>Buy Now
</button>
<button class="btn btn-sm btn-outline-primary add-to-cart-btn"
data-product-id="{{ prod_id }}"
data-product-name="{{ prod_name }}"
data-category="{{ category_id }}"
data-unit-price="{{ base_price }}"
data-provider-id="{{ provider_id }}"
data-provider-name="{{ provider_name }}"
data-quantity="1"
title="Buy instantly with your wallet balance">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
<a href="/products/{{ prod_id }}" class="btn btn-sm btn-outline-primary">
View Details
</a>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
No services available at the moment.
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script type="application/json" id="marketplace-dashboard-hydration">{}</script>
<script src="/static/js/marketplace_dashboard.js"></script>
{% endblock %}

View File

@@ -0,0 +1,320 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Mycelium Gateways{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>Mycelium Gateways</h1>
<p class="lead">Connect to the internet securely through the Mycelium network.</p>
<!-- Filter and Search Section -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Filter Gateways</h5>
<form class="row g-3" id="filterForm" method="GET">
<div class="col-md-3">
<label for="locationFilter" class="form-label">Location</label>
<select name="location" id="locationFilter" class="form-select">
<option value="">All Locations</option>
<option value="Belgium">Belgium</option>
<option value="Austria">Austria</option>
<option value="Switzerland">Switzerland</option>
<option value="Netherlands">Netherlands</option>
<option value="Germany">Germany</option>
<option value="France">France</option>
<option value="UK">UK</option>
<option value="Canada">Canada</option>
<option value="USA">USA</option>
<option value="Japan">Japan</option>
</select>
</div>
<div class="col-md-3">
<label for="speedFilter" class="form-label">Minimum Speed</label>
<select name="bandwidth_mbps" id="speedFilter" class="form-select">
<option value="">Any</option>
<option value="100">100+ Mbps</option>
<option value="500">500+ Mbps</option>
<option value="1000">1+ Gbps</option>
<option value="2500">2.5+ Gbps</option>
</select>
</div>
<div class="col-md-3">
<label for="minPriceFilter" class="form-label">Min Price ($)</label>
<input type="number" name="min_price" id="minPriceFilter" class="form-control"
value="" placeholder="0" min="0" step="1">
</div>
<div class="col-md-3">
<label for="maxPriceFilter" class="form-label">Max Price ($)</label>
<input type="number" name="max_price" id="maxPriceFilter" class="form-control"
value="" placeholder="300" min="0" step="1">
</div>
<div class="col-md-6 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
<a href="/marketplace/gateways" class="btn btn-outline-secondary">Clear</a>
</div>
</form>
</div>
</div>
<!-- Available Gateways Section -->
<div class="row">
{% if gateway_products and gateway_products | length > 0 %}
{% for product_data in gateway_products %}
<div class="col-lg-6 mb-4">
<div class="marketplace-item">
<div class="d-flex justify-content-between align-items-start mb-2">
<h4 class="mb-0">
<a href="/products/{{ product_data.product.id }}" class="text-decoration-none text-dark">
{{ product_data.product.name }}
</a>
</h4>
{% if product_data.product.metadata.featured %}
<span class="badge bg-warning">Featured</span>
{% elif product_data.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product_data.product.availability }}</span>
{% endif %}
</div>
<!-- Gateway Status -->
<div class="d-flex align-items-center mb-3">
<div class="gateway-status-indicator bg-success me-2" title="Online"></div>
<span>Online -
{% if product_data.product.attributes.uptime_sla %}
{{ product_data.product.attributes.uptime_sla.value }}
{% else %}
99.9%
{% endif %}
uptime in last 30 days
</span>
</div>
<!-- Product Specifications -->
<div class="row mb-3">
{% if product_data.product.attributes.bandwidth_mbps %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-speedometer me-2"></i>
<span>Speed: {{ product_data.product.attributes.bandwidth_mbps.value }} Mbps</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.uptime_sla %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-shield-check me-2"></i>
<span>SLA: {{ product_data.product.attributes.uptime_sla.value }} Uptime</span>
</div>
</div>
{% endif %}
</div>
<!-- Additional Info -->
<div class="row mb-3">
{% if product_data.product.metadata.location %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-geo-alt me-2"></i>
<span>Location: {{ product_data.product.metadata.location }}</span>
</div>
</div>
{% endif %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-clock-history me-2"></i>
<span>Provider: {{ product_data.product.provider_name }}</span>
</div>
</div>
</div>
<!-- Description -->
<p class="mb-4">{{ product_data.product.description | truncate(length=120) }}</p>
<!-- Features (if available in tags) -->
{% if product_data.product.metadata.tags and product_data.product.metadata.tags | length > 0 %}
<div class="mb-3">
<h6>Features:</h6>
<ul class="small">
{% for tag in product_data.product.metadata.tags %}
<li>{{ tag | title }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<!-- Price and Actions -->
<div class="d-flex justify-content-between align-items-center">
<div class="price-container">
<span class="text-muted">Price:</span>
<span class="text-primary fw-bold">{{ product_data.formatted_price }}</span>
</div>
<div class="btn-group">
<button class="btn btn-success btn-sm buy-now-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}"
data-category="gateways">
<i class="bi bi-lightning-fill me-1"></i>Buy Now
</button>
<button class="btn btn-outline-primary btn-sm add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
<a href="/products/{{ product_data.product.id }}" class="btn btn-outline-secondary btn-sm">View Details</a>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="text-center py-5">
<i class="bi bi-router display-1 text-muted"></i>
<h4 class="mt-3">No Gateways Available</h4>
<p class="text-muted">Check back later for new gateway services.</p>
</div>
</div>
{% endif %}
</div>
<!-- Gateway Configuration Guide -->
<div class="card mt-5">
<div class="card-body">
<h3 class="card-title">How to Configure Your Gateway</h3>
<p>Connecting your applications to a Mycelium Gateway is simple:</p>
<ol>
<li>Choose a gateway from the options above</li>
<li>Click the "Configure" button to start the setup process</li>
<li>Register or select a <a href="/marketplace/names">Mycelium Name</a> for your gateway</li>
<li>Configure domain routing and access controls</li>
<li>Connect your application by updating the configuration with the provided Mycelium address</li>
</ol>
<div class="alert alert-info mt-3">
<i class="bi bi-info-circle me-2"></i>
Need help setting up your gateway? <a href="/marketplace/services">Our technical experts</a> can assist with configuration and optimization.
</div>
</div>
</div>
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<nav aria-label="Gateway pages" class="mt-4">
<ul class="pagination justify-content-center">
<!-- Previous Page -->
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
{% if pagination.has_previous %}
<a class="page-link" href="?page={{ pagination.previous_page }}">Previous</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Previous</span>
{% endif %}
</li>
<!-- Page 1 -->
<li class="page-item {% if pagination.current_page == 0 %}active{% endif %}">
{% if pagination.current_page == 0 %}
<span class="page-link">1</span>
{% else %}
<a class="page-link" href="?page=0">1</a>
{% endif %}
</li>
<!-- Page 2 (if exists) -->
{% if pagination.total_pages > 1 %}
<li class="page-item {% if pagination.current_page == 1 %}active{% endif %}">
{% if pagination.current_page == 1 %}
<span class="page-link">2</span>
{% else %}
<a class="page-link" href="?page=1">2</a>
{% endif %}
</li>
{% endif %}
<!-- Page 3 (if exists) -->
{% if pagination.total_pages > 2 %}
<li class="page-item {% if pagination.current_page == 2 %}active{% endif %}">
{% if pagination.current_page == 2 %}
<span class="page-link">3</span>
{% else %}
<a class="page-link" href="?page=2">3</a>
{% endif %}
</li>
{% endif %}
<!-- Page 4 (if exists) -->
{% if pagination.total_pages > 3 %}
<li class="page-item {% if pagination.current_page == 3 %}active{% endif %}">
{% if pagination.current_page == 3 %}
<span class="page-link">4</span>
{% else %}
<a class="page-link" href="?page=3">4</a>
{% endif %}
</li>
{% endif %}
<!-- Page 5 (if exists) -->
{% if pagination.total_pages > 4 %}
<li class="page-item {% if pagination.current_page == 4 %}active{% endif %}">
{% if pagination.current_page == 4 %}
<span class="page-link">5</span>
{% else %}
<a class="page-link" href="?page=4">5</a>
{% endif %}
</li>
{% endif %}
<!-- Next Page -->
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
{% if pagination.has_next %}
<a class="page-link" href="?page={{ pagination.next_page }}">Next</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Next</span>
{% endif %}
</li>
</ul>
</nav>
<!-- Results Info -->
<div class="text-center text-muted mt-2">
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
({{ pagination.total_count }} total gateways)
</div>
{% endif %}
</div>
<style>
.gateway-status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
}
.gateway-status-indicator.bg-success {
background-color: #28a745;
}
.gateway-status-indicator.bg-warning {
background-color: #ffc107;
}
.gateway-status-indicator.bg-danger {
background-color: #dc3545;
}
.spec-item {
margin-bottom: 5px;
}
.price-container {
font-size: 1.5rem;
}
</style>
<script src="/static/js/marketplace-category.js"></script>
{% endblock %}

View File

@@ -0,0 +1,160 @@
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<div class="row">
<!-- Sidebar Toggle Button (mobile only) -->
<button class="btn sidebar-toggle d-md-none" id="sidebarToggleBtn" aria-label="Toggle sidebar navigation" aria-expanded="false" aria-controls="sidebar">
<i class="bi bi-list"></i> Menu
</button>
<!-- Sidebar -->
<div class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse" id="sidebar">
<div class="position-sticky pt-3">
<h5 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mb-1 text-muted">
<span>Marketplace</span>
</h5>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link {% if active_section == 'dashboard' %}active{% endif %}" href="/marketplace">
<i class="bi bi-speedometer2 me-1"></i>
Overview
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_section == 'statistics' %}active{% endif %}" href="/marketplace/statistics">
<i class="bi bi-graph-up me-1"></i>
Statistics
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_section == 'compute_resources' %}active{% endif %}" href="/marketplace/compute">
<i class="bi bi-cpu me-1"></i>
Compute Resources
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_section == 'three_nodes' %}active{% endif %}" href="/marketplace/3nodes">
<i class="bi bi-hdd-rack me-1"></i>
3Nodes
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_section == 'gateways' %}active{% endif %}" href="/marketplace/gateways">
<i class="bi bi-globe me-1"></i>
Mycelium Gateways
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_section == 'applications' %}active{% endif %}" href="/marketplace/applications">
<i class="bi bi-app me-1"></i>
Application Solutions
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_section == 'services' %}active{% endif %}" href="/marketplace/services">
<i class="bi bi-person-workspace me-1"></i>
Human Energy Services
</a>
</li>
</ul>
</div>
</div>
<!-- Main Content -->
<div class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content-wrapper position-relative">
<!-- Sidebar Backdrop (mobile only) - positioned here so it doesn't cover the sidebar -->
<div class="sidebar-backdrop d-md-none" id="sidebarBackdrop"></div>
{% block marketplace_content %}
<!-- Content will be injected here by child templates -->
{% endblock %}
</div>
</div>
</div>
{% endblock %}
{% block head %}
<!-- Add Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css">
<style>
.sidebar {
position: fixed;
top: 56px; /* Navbar height */
bottom: 0;
left: 0;
z-index: 100;
padding: 20px 0 0;
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
height: 100%; /* Full height */
overflow-y: auto;
}
/* Footer should only appear on the right side */
@media (min-width: 768px) {
.footer {
margin-left: 25%; /* Matches the width of col-md-3 */
}
}
@media (min-width: 992px) {
.footer {
margin-left: 16.666667%; /* Matches the width of col-lg-2 */
}
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
}
.sidebar .nav-link.active {
color: #2470dc;
}
.dashboard-section {
padding: 20px;
margin-bottom: 20px;
border-radius: 5px;
background-color: #f8f9fa;
box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075);
}
.marketplace-item {
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
margin-bottom: 20px;
background-color: white;
transition: all 0.3s ease;
}
.marketplace-item:hover {
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
transform: translateY(-5px);
}
.badge-category {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
margin-right: 5px;
}
/* Adjust for fixed navbar */
main.py-4 {
padding-top: 4.5rem !important;
}
/* Removed conflicting media query */
</style>
{% endblock %}
{% block scripts %}
<script src="/static/js/marketplace_layout.js"></script>
<!-- buy-now.js is now included in base.html -->
{% endblock %}

View File

@@ -0,0 +1,442 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order Confirmation - Project Mycelium</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.navbar-brand img {
height: 30px;
}
.success-icon {
font-size: 4rem;
color: #28a745;
}
.confirmation-card {
background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%);
border: 2px solid #28a745;
border-radius: 12px;
}
.order-details {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.next-steps {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border: 1px solid #2196f3;
border-radius: 8px;
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn-primary {
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #0b5ed7 0%, #0a58ca 100%);
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item + .breadcrumb-item::before {
content: "";
font-weight: bold;
}
.status-badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
}
.status-confirmed {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.order-tracking-timeline {
position: relative;
}
.tracking-step {
display: flex;
align-items: center;
margin-bottom: 1rem;
position: relative;
}
.tracking-step:not(:last-child)::after {
content: '';
position: absolute;
left: 12px;
top: 32px;
width: 2px;
height: 20px;
background: #dee2e6;
}
.tracking-step.completed::after {
background: #28a745;
}
.tracking-step.active::after {
background: #007bff;
}
.step-icon {
width: 24px;
height: 24px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
background: white;
}
.step-content {
flex: 1;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo_dark.png" alt="ThreeFold Logo" class="me-2">
<span>Project Mycelium</span>
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/marketplace">
<i class="bi bi-shop me-1"></i>Marketplace
</a>
<a class="nav-link" href="/orders">
<i class="bi bi-list-ul me-1"></i>My Orders
</a>
</div>
</div>
</nav>
<div class="container py-4">
<!-- Success Message -->
<div class="row justify-content-center mb-4">
<div class="col-md-8">
<div class="confirmation-card p-5 text-center">
<i class="bi bi-check-circle success-icon mb-3"></i>
<h1 class="h2 mb-3">Order Confirmed!</h1>
<p class="lead mb-4">
Thank you for your order. Your ThreeFold resources are being prepared for deployment.
</p>
{% if order %}
<div class="row">
<div class="col-md-6">
<div class="mb-2">
<strong>Order Number:</strong>
</div>
<div class="h5 text-primary">#{{ order.order_id }}</div>
</div>
<div class="col-md-6">
<div class="mb-2">
<strong>Confirmation Code:</strong>
</div>
<div class="h5 text-success">{{ order.confirmation_number }}</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<!-- Order Details -->
<div class="col-lg-8">
{% if order %}
<div class="order-details p-4 mb-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h3><i class="bi bi-receipt me-2"></i>Order Details</h3>
<span class="status-badge status-confirmed">
<i class="bi bi-check-circle me-1"></i>{{ order.status }}
</span>
</div>
<div class="row mb-4">
<div class="col-md-6">
<div class="mb-3">
<strong class="text-muted">Order Date:</strong>
<div>{{ order.created_at }}</div>
</div>
<div class="mb-3">
<strong class="text-muted">Payment Method:</strong>
<div>{{ order.payment_method }}</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<strong class="text-muted">Total Amount:</strong>
<div class="h5 text-primary">{{ order.total }}</div>
</div>
<div class="mb-3">
<strong class="text-muted">Currency:</strong>
<div>{{ order.currency }}</div>
</div>
</div>
</div>
<h5 class="mb-3">Ordered Items</h5>
{% for item in order.items %}
<div class="d-flex align-items-center p-3 border rounded mb-3">
<div class="me-3">
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
{% if item.product_category == "compute" %}
<i class="bi bi-cpu text-primary"></i>
{% elif item.product_category == "hardware" %}
<i class="bi bi-hdd-rack text-success"></i>
{% elif item.product_category == "gateways" %}
<i class="bi bi-globe text-info"></i>
{% elif item.product_category == "applications" %}
<i class="bi bi-app text-warning"></i>
{% elif item.product_category == "services" %}
<i class="bi bi-person-workspace text-secondary"></i>
{% else %}
<i class="bi bi-box text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-1">{{ item.product_name }}</h6>
<small class="text-muted">{{ item.provider_name }}</small>
<div class="mt-1">
<span class="badge bg-light text-dark">Qty: {{ item.quantity }}</span>
<span class="badge bg-secondary ms-1">{{ item.product_category|title }}</span>
</div>
</div>
<div class="text-end">
<div class="fw-bold">{{ item.unit_price }}</div>
<small class="text-muted">per unit</small>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<!-- Next Steps -->
<div class="next-steps p-4">
<h5 class="mb-3">
<i class="bi bi-arrow-right-circle me-2"></i>What's Next?
</h5>
<div class="row">
<div class="col-md-6">
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-1-circle fs-4 text-primary"></i>
</div>
<div>
<div class="fw-bold">Processing</div>
<small class="text-muted">Your order is being processed and resources are being allocated.</small>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-2-circle fs-4 text-info"></i>
</div>
<div>
<div class="fw-bold">Deployment</div>
<small class="text-muted">Resources will be deployed to the ThreeFold Grid within 24 hours.</small>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-3-circle fs-4 text-success"></i>
</div>
<div>
<div class="fw-bold">Access</div>
<small class="text-muted">You'll receive access credentials and connection details via email.</small>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-4-circle fs-4 text-warning"></i>
</div>
<div>
<div class="fw-bold">Support</div>
<small class="text-muted">Our support team is available 24/7 to help with any questions.</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Actions Sidebar -->
<div class="col-lg-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-gear me-2"></i>Quick Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="/orders" class="btn btn-primary">
<i class="bi bi-list-ul me-2"></i>View All Orders
</a>
<a href="/marketplace" class="btn btn-outline-primary">
<i class="bi bi-shop me-2"></i>Continue Shopping
</a>
<a href="/dashboard" class="btn btn-outline-secondary">
<i class="bi bi-speedometer2 me-2"></i>Go to Dashboard
</a>
</div>
<hr>
<h6 class="mb-3">Need Help?</h6>
<div class="d-grid gap-2">
<a href="/docs" class="btn btn-sm btn-outline-primary">
<i class="bi bi-book me-1"></i>Docs
</a>
<a href="https://threefoldfaq.crisp.help/en/" class="btn btn-sm btn-outline-success" target="_blank">
<i class="bi bi-chat-dots me-1"></i>Live Chat
</a>
<a href="mailto:support@threefold.io" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-envelope me-1"></i>Email Support
</a>
</div>
</div>
</div>
<!-- Order Tracking -->
<div class="card mt-4">
<div class="card-header bg-light">
<h6 class="mb-0">
<i class="bi bi-truck me-2"></i>Track Your Order
</h6>
</div>
<div class="card-body">
<p class="small text-muted mb-3">
You can track the status of your order and deployment progress in real-time.
</p>
<!-- Mock Order Tracking Timeline -->
{% if order %}
<div class="order-tracking-timeline mb-3">
<div class="tracking-step completed">
<div class="step-icon">
<i class="bi bi-check-circle-fill text-success"></i>
</div>
<div class="step-content">
<div class="fw-bold">Order Confirmed</div>
<small class="text-muted">{{ order.created_at }}</small>
</div>
</div>
<div class="tracking-step active">
<div class="step-icon">
<i class="bi bi-gear-fill text-primary"></i>
</div>
<div class="step-content">
<div class="fw-bold">Processing</div>
<small class="text-muted">Resources being allocated</small>
</div>
</div>
<div class="tracking-step pending">
<div class="step-icon">
<i class="bi bi-cloud-arrow-up text-muted"></i>
</div>
<div class="step-content">
<div class="fw-bold">Deploying</div>
<small class="text-muted">Setting up your resources</small>
</div>
</div>
<div class="tracking-step pending">
<div class="step-icon">
<i class="bi bi-check-all text-muted"></i>
</div>
<div class="step-content">
<div class="fw-bold">Ready</div>
<small class="text-muted">Resources available</small>
</div>
</div>
</div>
<div class="d-grid">
<a href="/orders/{{ order.order_id }}" class="btn btn-outline-primary btn-sm">
<i class="bi bi-eye me-1"></i>View Full Order Details
</a>
</div>
{% else %}
<div class="d-grid">
<a href="/orders" class="btn btn-outline-primary btn-sm">
<i class="bi bi-list-ul me-1"></i>View Orders
</a>
</div>
{% endif %}
</div>
</div>
<!-- Email Confirmation -->
<div class="card mt-4">
<div class="card-body text-center">
<i class="bi bi-envelope-check fs-1 text-success mb-2"></i>
<h6>Email Confirmation Sent</h6>
<p class="small text-muted">
A detailed confirmation has been sent to your email address with all order information and next steps.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
console.log('Order confirmation page loaded');
// Auto-scroll to top
window.scrollTo(0, 0);
// Show success animation
const successIcon = document.querySelector('.success-icon');
if (successIcon) {
successIcon.style.transform = 'scale(0)';
setTimeout(() => {
successIcon.style.transition = 'transform 0.5s ease-out';
successIcon.style.transform = 'scale(1)';
}, 300);
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,311 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order Details - Project Mycelium</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.navbar-brand img {
height: 30px;
}
.order-header {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border: 2px solid #2196f3;
border-radius: 12px;
}
.order-details {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.status-badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
}
.status-confirmed {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-pending {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.status-processing {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.status-failed {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn-primary {
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #0b5ed7 0%, #0a58ca 100%);
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item + .breadcrumb-item::before {
content: "";
font-weight: bold;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo_dark.png" alt="ThreeFold Logo" class="me-2">
<span>Project Mycelium</span>
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/marketplace">
<i class="bi bi-shop me-1"></i>Marketplace
</a>
<a class="nav-link active" href="/orders">
<i class="bi bi-list-ul me-1"></i>My Orders
</a>
</div>
</div>
</nav>
<div class="container py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item"><a href="/marketplace" class="text-decoration-none">Marketplace</a></li>
<li class="breadcrumb-item"><a href="/orders" class="text-decoration-none">Orders</a></li>
<li class="breadcrumb-item active" aria-current="page">Order Details</li>
</ol>
</nav>
{% if order %}
<!-- Order Header -->
<div class="order-header p-4 mb-4">
<div class="row align-items-center">
<div class="col-md-8">
<h1 class="h2 mb-2">
<i class="bi bi-receipt me-2"></i>Order #{{ order.order_id }}
</h1>
<p class="mb-0 text-muted">
<i class="bi bi-calendar me-1"></i>Placed on {{ order.created_at }}
</p>
</div>
<div class="col-md-4 text-md-end">
<span class="status-badge status-{{ order.status|lower }}">
<i class="bi bi-check-circle me-1"></i>{{ order.status }}
</span>
</div>
</div>
</div>
<div class="row">
<!-- Order Details -->
<div class="col-lg-8">
<div class="order-details p-4 mb-4">
<h3 class="mb-4">
<i class="bi bi-box-seam me-2"></i>Order Items
</h3>
{% for item in order.items %}
<div class="d-flex align-items-center p-3 border rounded mb-3">
<div class="me-3">
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
{% if item.product_category == "compute" %}
<i class="bi bi-cpu text-primary"></i>
{% elif item.product_category == "hardware" %}
<i class="bi bi-hdd-rack text-success"></i>
{% elif item.product_category == "gateways" %}
<i class="bi bi-globe text-info"></i>
{% elif item.product_category == "applications" %}
<i class="bi bi-app text-warning"></i>
{% elif item.product_category == "services" %}
<i class="bi bi-person-workspace text-secondary"></i>
{% else %}
<i class="bi bi-box text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-1">{{ item.product_name }}</h6>
<small class="text-muted">{{ item.provider_name }}</small>
<div class="mt-1">
<span class="badge bg-light text-dark">Qty: {{ item.quantity }}</span>
<span class="badge bg-secondary ms-1">{{ item.product_category|title }}</span>
</div>
</div>
<div class="text-end">
<div class="fw-bold">{{ item.unit_price }}</div>
<small class="text-muted">per unit</small>
<div class="text-primary fw-bold">{{ item.total_price }}</div>
<small class="text-muted">total</small>
</div>
</div>
{% endfor %}
<!-- Order Summary -->
<div class="border-top pt-3 mt-4">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<strong class="text-muted">Payment Method:</strong>
<div>{{ order.payment_method }}</div>
</div>
<div class="mb-3">
<strong class="text-muted">Currency:</strong>
<div>{{ order.currency }}</div>
</div>
</div>
<div class="col-md-6">
<div class="text-md-end">
<div class="d-flex justify-content-between mb-2">
<span>Subtotal:</span>
<span>{{ order.subtotal }}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Platform fee:</span>
<span class="text-muted">Free</span>
</div>
<hr>
<div class="d-flex justify-content-between">
<span class="fw-bold fs-5">Total:</span>
<span class="fw-bold text-primary fs-4">{{ order.total }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Actions Sidebar -->
<div class="col-lg-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-gear me-2"></i>Order Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="/orders" class="btn btn-primary">
<i class="bi bi-arrow-left me-2"></i>Back to Orders
</a>
<a href="/marketplace" class="btn btn-outline-primary">
<i class="bi bi-shop me-2"></i>Continue Shopping
</a>
<button class="btn btn-outline-secondary js-print">
<i class="bi bi-printer me-2"></i>Print Order
</button>
</div>
<hr>
<h6 class="mb-3">Need Help?</h6>
<div class="d-grid gap-2">
<a href="/docs" class="btn btn-sm btn-outline-primary">
<i class="bi bi-book me-1"></i>Docs
</a>
<a href="https://threefoldfaq.crisp.help/en/" class="btn btn-sm btn-outline-success" target="_blank">
<i class="bi bi-chat-dots me-1"></i>Live Chat
</a>
<a href="mailto:support@threefold.io" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-envelope me-1"></i>Email Support
</a>
</div>
</div>
</div>
<!-- Order Status Timeline -->
<div class="card mt-4">
<div class="card-header bg-light">
<h6 class="mb-0">
<i class="bi bi-clock-history me-2"></i>Order Timeline
</h6>
</div>
<div class="card-body">
<div class="timeline">
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-check-circle-fill text-success fs-5"></i>
</div>
<div>
<div class="fw-bold">Order Placed</div>
<small class="text-muted">{{ order.created_at }}</small>
</div>
</div>
{% if order.status == "Confirmed" %}
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-check-circle-fill text-success fs-5"></i>
</div>
<div>
<div class="fw-bold">Payment Confirmed</div>
<small class="text-muted">Payment processed successfully</small>
</div>
</div>
<div class="d-flex mb-3">
<div class="me-3">
<i class="bi bi-circle text-muted fs-5"></i>
</div>
<div>
<div class="text-muted">Deployment</div>
<small class="text-muted">Resources being deployed</small>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-exclamation-triangle fs-1 text-warning mb-3"></i>
<h3>Order Not Found</h3>
<p class="text-muted">The requested order could not be found.</p>
<a href="/orders" class="btn btn-primary">View All Orders</a>
</div>
{% endif %}
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script type="application/json" id="order-detail-data">{}</script>
<script src="/static/js/print-utils.js"></script>
</body>
</html>

View File

@@ -0,0 +1,210 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order Invoice - Project Mycelium</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body { background-color: #f8f9fa; }
.navbar-brand img { height: 30px; }
.invoice-header {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border: 2px solid #2196f3;
border-radius: 12px;
}
.invoice-details {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.invoice-number { font-size: 1.5rem; font-weight: bold; color: #2196f3; }
.card { border: none; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.btn-primary { background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%); border: none; }
.btn-primary:hover { background: linear-gradient(135deg, #0b5ed7 0%, #0a58ca 100%); }
.invoice-table th { background-color: #f8f9fa; border-top: none; }
.total-amount { font-size: 2rem; font-weight: bold; color: #2196f3; }
@media print {
.no-print { display: none !important; }
body { background-color: white !important; }
.card, .invoice-details { box-shadow: none !important; border: 1px solid #dee2e6 !important; }
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark no-print">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo_dark.png" alt="ThreeFold Logo" class="me-2">
<span>Project Mycelium</span>
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/marketplace">
<i class="bi bi-shop me-1"></i>Marketplace
</a>
<a class="nav-link" href="/orders">
<i class="bi bi-list-ul me-1"></i>My Orders
</a>
</div>
</div>
</nav>
<div class="container py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4 no-print">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item"><a href="/marketplace" class="text-decoration-none">Marketplace</a></li>
<li class="breadcrumb-item"><a href="/orders" class="text-decoration-none">Orders</a></li>
<li class="breadcrumb-item active" aria-current="page">Order Invoice</li>
</ol>
</nav>
{% if order %}
<!-- Invoice Header -->
<div class="invoice-header p-4 mb-4">
<div class="row align-items-center">
<div class="col-md-8">
<h1 class="h2 mb-2">
<i class="bi bi-receipt me-2"></i>THREEFOLD MARKETPLACE INVOICE
</h1>
<div class="invoice-number">INV-{{ order.order_id }}</div>
</div>
<div class="col-md-4 text-md-end">
<div class="mb-2"><strong>Invoice Date:</strong> {{ invoice_date }}</div>
<div><strong>Due Date:</strong> {{ due_date }}</div>
</div>
</div>
</div>
<div class="row">
<!-- Invoice Details -->
<div class="col-lg-8">
<div class="invoice-details p-4 mb-4">
<!-- Seller and Bill To -->
<div class="row mb-4">
<div class="col-md-6">
<h6 class="text-muted mb-2">SOLD BY:</h6>
<div class="fw-bold">Project Mycelium</div>
<div>support@threefold.io</div>
</div>
<div class="col-md-6">
<h6 class="text-muted mb-2">BILL TO:</h6>
{% if user %}
<div class="fw-bold">{{ user.email }}</div>
{% if user.name %}<div>{{ user.name }}</div>{% endif %}
{% if user.country %}<div>{{ user.country }}</div>{% endif %}
{% else %}
<div class="fw-bold">Account Holder</div>
{% endif %}
</div>
</div>
<!-- Items Table -->
<div class="mb-4">
<h6 class="text-muted mb-3">ITEMS:</h6>
<table class="table invoice-table">
<thead>
<tr>
<th>Product</th>
<th class="text-center">Qty</th>
<th class="text-center">Unit Price</th>
<th class="text-end">Amount</th>
</tr>
</thead>
<tbody>
{% for item in order.items %}
<tr>
<td>
<div class="fw-semibold">{{ item.product_name }}</div>
<small class="text-muted">{{ item.provider_name }} • {{ item.product_category|title }}</small>
</td>
<td class="text-center">{{ item.quantity }}</td>
<td class="text-center">{{ item.unit_price }}</td>
<td class="text-end">{{ item.total_price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Totals -->
<div class="border-top pt-4 mt-4">
<div class="row">
<div class="col-md-6">
<h6 class="text-muted mb-3">PAYMENT DETAILS:</h6>
<p class="mb-2">Payment Method: <strong>{{ order.payment_method }}</strong></p>
<p class="mb-2">Currency: <strong>{{ order.currency }}</strong></p>
<p class="mb-0">Invoice generated: <small class="text-muted">{{ generated_date }}</small></p>
</div>
<div class="col-md-6">
<div class="text-md-end">
<div class="d-flex justify-content-between mb-2">
<span>Subtotal:</span>
<span>{{ order.subtotal }}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Platform fee:</span>
<span class="text-muted">Free</span>
</div>
<hr>
<div class="d-flex justify-content-between">
<span class="fw-bold fs-5">TOTAL AMOUNT:</span>
<span class="total-amount">{{ order.total }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Actions Sidebar -->
<div class="col-lg-4 no-print">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="bi bi-gear me-2"></i>Invoice Actions</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="/orders/{{ order.order_id }}" class="btn btn-primary">
<i class="bi bi-arrow-left me-2"></i>Back to Order
</a>
<button class="btn btn-success js-print">
<i class="bi bi-printer me-2"></i>Print Invoice
</button>
</div>
<hr>
<h6 class="mb-3">Need Help?</h6>
<div class="d-grid gap-2">
<a href="/docs" class="btn btn-sm btn-outline-primary">
<i class="bi bi-book me-1"></i>Documentation
</a>
<a href="https://threefoldfaq.crisp.help/en/" class="btn btn-sm btn-outline-success" target="_blank">
<i class="bi bi-chat-dots me-1"></i>Live Chat
</a>
<a href="mailto:support@threefold.io" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-envelope me-1"></i>Email Support
</a>
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-exclamation-triangle fs-1 text-warning mb-3"></i>
<h3>Order Not Found</h3>
<p class="text-muted">The requested order could not be found.</p>
<a href="/orders" class="btn btn-primary">View All Orders</a>
</div>
{% endif %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script type="application/json" id="invoice-data">{}</script>
<script src="/static/js/print-utils.js"></script>
</body>
</html>

View File

@@ -0,0 +1,395 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Orders - Project Mycelium</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.navbar-brand img {
height: 30px;
}
.order-card {
transition: all 0.3s ease;
border: none;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.order-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.status-badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
font-size: 0.875rem;
}
.status-pending {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.status-confirmed {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-processing {
background: #cce5ff;
color: #004085;
border: 1px solid #99d6ff;
}
.status-deployed {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.status-completed {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-cancelled {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.empty-state {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.filter-card {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
position: sticky;
top: 20px;
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item + .breadcrumb-item::before {
content: "";
font-weight: bold;
}
.btn-primary {
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #0b5ed7 0%, #0a58ca 100%);
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo_dark.png" alt="ThreeFold Logo" class="me-2">
<span>Project Mycelium</span>
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/marketplace">
<i class="bi bi-shop me-1"></i>Marketplace
</a>
<a class="nav-link" href="/cart">
<i class="bi bi-cart3 me-1"></i>Cart
</a>
<a class="nav-link active" href="/orders">
<i class="bi bi-list-ul me-1"></i>My Orders
</a>
</div>
</div>
</nav>
<div class="container py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item"><a href="/marketplace" class="text-decoration-none">Marketplace</a></li>
<li class="breadcrumb-item active" aria-current="page">My Orders</li>
</ol>
</nav>
<!-- Page Header -->
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-4 border-bottom">
<h1 class="h2 mb-0">
<i class="bi bi-list-ul me-2 text-primary"></i>My Orders
</h1>
<div class="btn-toolbar">
<a href="/marketplace" class="btn btn-primary">
<i class="bi bi-shop me-1"></i>Continue Shopping
</a>
</div>
</div>
<div class="row">
<!-- Orders List -->
<div class="col-lg-8">
{% if orders and orders|length > 0 %}
{% for order in orders %}
<div class="order-card card mb-4">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<div>
<h5 class="mb-1">Order #{{ order.order_id }}</h5>
<small class="text-muted">Placed on {{ order.created_at }}</small>
</div>
<span class="status-badge status-{{ order.status|lower }}">
{% if order.status == "Pending" %}
<i class="bi bi-clock me-1"></i>
{% elif order.status == "Confirmed" %}
<i class="bi bi-check-circle me-1"></i>
{% elif order.status == "Processing" %}
<i class="bi bi-gear me-1"></i>
{% elif order.status == "Deployed" %}
<i class="bi bi-cloud-check me-1"></i>
{% elif order.status == "Completed" %}
<i class="bi bi-check-all me-1"></i>
{% elif order.status == "Cancelled" %}
<i class="bi bi-x-circle me-1"></i>
{% endif %}
{{ order.status }}
</span>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-8">
<h6 class="mb-3">Items ({{ order.items|length }})</h6>
{% for item in order.items %}
<div class="d-flex align-items-center mb-2">
<div class="me-3">
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
{% if item.product_category == "compute" %}
<i class="bi bi-cpu text-primary"></i>
{% elif item.product_category == "hardware" %}
<i class="bi bi-hdd-rack text-success"></i>
{% elif item.product_category == "gateways" %}
<i class="bi bi-globe text-info"></i>
{% elif item.product_category == "applications" %}
<i class="bi bi-app text-warning"></i>
{% elif item.product_category == "services" %}
<i class="bi bi-person-workspace text-secondary"></i>
{% else %}
<i class="bi bi-box text-muted"></i>
{% endif %}
</div>
</div>
<div class="flex-grow-1">
<div class="fw-bold">{{ item.product_name }}</div>
<small class="text-muted">{{ item.provider_name }} • Qty: {{ item.quantity }}</small>
</div>
<div class="text-end">
<div class="fw-bold">{{ item.total_price }}</div>
</div>
</div>
{% endfor %}
</div>
<div class="col-md-4">
<div class="text-end">
<div class="mb-2">
<strong class="text-muted">Total Amount:</strong>
</div>
<div class="h4 text-primary mb-3">{{ order.total }}</div>
<div class="mb-2">
<strong class="text-muted">Payment:</strong>
</div>
<div class="mb-3">{{ order.payment_method }}</div>
<div class="d-grid gap-2">
<a href="/orders/{{ order.order_id }}" class="btn btn-outline-primary btn-sm">
<i class="bi bi-eye me-1"></i>View Details
</a>
{% if order.status == "Deployed" or order.status == "Completed" %}
<a href="#" class="btn btn-outline-success btn-sm">
<i class="bi bi-play-circle me-1"></i>Access Resources
</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% if order.confirmation_number %}
<div class="card-footer bg-light">
<small class="text-muted">
<i class="bi bi-shield-check me-1"></i>
Confirmation: <strong>{{ order.confirmation_number }}</strong>
</small>
</div>
{% endif %}
</div>
{% endfor %}
{% else %}
<!-- Empty State -->
<div class="empty-state p-5 text-center">
<i class="bi bi-bag-x display-1 text-muted mb-4"></i>
<h3 class="text-muted mb-3">No orders yet</h3>
<p class="text-muted mb-4">
You haven't placed any orders yet. Explore our marketplace to find amazing ThreeFold resources and services.
</p>
<a href="/marketplace" class="btn btn-primary btn-lg">
<i class="bi bi-shop me-2"></i>Browse Marketplace
</a>
</div>
{% endif %}
</div>
<!-- Filters & Info Sidebar -->
<div class="col-lg-4">
<div class="filter-card p-4 mb-4">
<h5 class="mb-3">
<i class="bi bi-funnel me-2"></i>Filter Orders
</h5>
<div class="mb-3">
<label for="statusFilter" class="form-label">Order Status</label>
<select class="form-select" id="statusFilter">
<option value="">All Statuses</option>
<option value="pending">Pending</option>
<option value="confirmed">Confirmed</option>
<option value="processing">Processing</option>
<option value="deployed">Deployed</option>
<option value="completed">Completed</option>
<option value="cancelled">Cancelled</option>
</select>
</div>
<div class="mb-3">
<label for="dateRange" class="form-label">Date Range</label>
<select class="form-select" id="dateRange">
<option value="">All Time</option>
<option value="7">Last 7 days</option>
<option value="30">Last 30 days</option>
<option value="90">Last 3 months</option>
<option value="365">Last year</option>
</select>
</div>
<div class="d-grid">
</div>
</div>
<!-- Order Statistics -->
<div class="filter-card p-4 mb-4">
<h6 class="mb-3">
<i class="bi bi-graph-up me-2"></i>Order Summary
</h6>
{% if orders %}
<div class="row text-center">
<div class="col-6 mb-3">
<div class="h4 text-primary">{{ orders|length }}</div>
<small class="text-muted">Total Orders</small>
</div>
<div class="col-6 mb-3">
<div class="h4 text-success">
{% set completed_count = 0 %}
{% for order in orders %}
{% if order.status == "Completed" %}
{% set completed_count = completed_count + 1 %}
{% endif %}
{% endfor %}
{{ completed_count }}
</div>
<small class="text-muted">Completed</small>
</div>
<div class="col-6">
<div class="h4 text-info">
{% set active_count = 0 %}
{% for order in orders %}
{% if order.status in ["Pending", "Confirmed", "Processing", "Deployed"] %}
{% set active_count = active_count + 1 %}
{% endif %}
{% endfor %}
{{ active_count }}
</div>
<small class="text-muted">Active</small>
</div>
<div class="col-6">
<div class="h4 text-warning">
{% set pending_count = 0 %}
{% for order in orders %}
{% if order.status == "Pending" %}
{% set pending_count = pending_count + 1 %}
{% endif %}
{% endfor %}
{{ pending_count }}
</div>
<small class="text-muted">Pending</small>
</div>
</div>
{% else %}
<div class="text-center text-muted">
<i class="bi bi-graph-up fs-1 mb-2"></i>
<div>No order data available</div>
</div>
{% endif %}
</div>
<!-- Quick Actions -->
<div class="filter-card p-4">
<h6 class="mb-3">
<i class="bi bi-lightning me-2"></i>Quick Actions
</h6>
<div class="d-grid gap-2">
<a href="/marketplace" class="btn btn-outline-primary btn-sm">
<i class="bi bi-shop me-1"></i>Browse Products
</a>
<a href="/cart" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-cart3 me-1"></i>View Cart
</a>
<a href="/dashboard" class="btn btn-outline-info btn-sm">
<i class="bi bi-speedometer2 me-1"></i>Dashboard
</a>
<button class="btn btn-outline-success btn-sm" data-action="export-orders">
<i class="bi bi-download me-1"></i>Export Orders
</button>
</div>
<hr>
<h6 class="mb-2">Need Help?</h6>
<div class="d-grid gap-1">
<a href="/docs" class="btn btn-sm btn-outline-primary">
<i class="bi bi-book me-1"></i>Docs
</a>
<a href="https://threefoldfaq.crisp.help/en/" class="btn btn-sm btn-outline-success" target="_blank">
<i class="bi bi-chat-dots me-1"></i>Contact Support
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script type="application/json" id="orders-data">{}</script>
<script src="/static/js/orders.js"></script>
</body>
</html>

View File

@@ -0,0 +1,293 @@
{% extends "marketplace/layout.html" %}
{% block title %}{{ product.product.name }} - Project Mycelium{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/marketplace">Marketplace</a></li>
<li class="breadcrumb-item">
<a href="/marketplace/{{ product.product.category_id }}">
{{ product.product.category_id | title }}
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">{{ product.product.name }}</li>
</ol>
</nav>
<div class="row">
<!-- Product Details -->
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<!-- Product Header -->
<div class="d-flex justify-content-between align-items-start mb-4">
<div>
<h1 class="h2 mb-2">{{ product.product.name }}</h1>
<div class="d-flex align-items-center mb-2">
<span class="badge bg-primary me-2">{{ product.product.category_id | title }}</span>
{% if product.product.metadata.featured %}
<span class="badge bg-warning me-2">Featured</span>
{% endif %}
{% if product.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product.product.availability }}</span>
{% endif %}
</div>
<p class="text-muted mb-0">
<i class="bi bi-building me-1"></i>
{{ product.product.provider_name }}
{% if product.product.metadata.location %}
<span class="ms-3">
<i class="bi bi-geo-alt me-1"></i>
{{ product.product.metadata.location }}
</span>
{% endif %}
</p>
</div>
<div class="text-end">
<div class="price-display">
<div class="h3 text-primary mb-0">{{ product.formatted_price }}</div>
{% if product.price.display_currency != product.price.base_currency %}
<small class="text-muted">
({{ product.price.base_amount }} {{ product.price.base_currency }})
</small>
{% endif %}
</div>
</div>
</div>
<!-- Product Description -->
<div class="mb-4">
<h5>Description</h5>
<p>{{ product.product.description }}</p>
</div>
<!-- Product Specifications -->
{% if product.product.attributes %}
<div class="mb-4">
<h5>Specifications</h5>
<div class="row">
{% for key, attr in product.product.attributes %}
<div class="col-md-6 mb-3">
<div class="spec-item">
{% if key == "cpu_cores" %}
<i class="bi bi-cpu me-2 text-primary"></i>
{% elif key == "memory_gb" %}
<i class="bi bi-memory me-2 text-primary"></i>
{% elif key == "storage_gb" %}
<i class="bi bi-hdd me-2 text-primary"></i>
{% elif key == "bandwidth_mbps" %}
<i class="bi bi-speedometer2 me-2 text-primary"></i>
{% elif key == "uptime_percentage" %}
<i class="bi bi-check-circle me-2 text-success"></i>
{% else %}
<i class="bi bi-info-circle me-2 text-info"></i>
{% endif %}
<strong>{{ key | replace(from="_", to=" ") | title }}:</strong>
<span class="ms-2">
{% if key == "memory_gb" or key == "storage_gb" %}
{{ attr.value }} GB
{% elif key == "bandwidth_mbps" %}
{{ attr.value }} Mbps
{% elif key == "uptime_percentage" %}
{{ attr.value }}%
{% else %}
{{ attr.value }}
{% endif %}
</span>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Product Tags -->
{% if product.product.metadata.tags %}
<div class="mb-4">
<h6>Tags</h6>
{% for tag in product.product.metadata.tags %}
<span class="badge bg-light text-dark me-1">{{ tag }}</span>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Add to Cart Card -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Purchase Options</h5>
<!-- Quantity Selector -->
<div class="mb-3">
<label for="quantity" class="form-label">Quantity</label>
<div class="input-group">
<button class="btn btn-outline-secondary" type="button" id="decreaseQty">-</button>
<input type="number" class="form-control text-center" id="quantity" value="1" min="1" max="10">
<button class="btn btn-outline-secondary" type="button" id="increaseQty">+</button>
</div>
</div>
<!-- Total Price Display -->
<div class="mb-3">
<div class="d-flex justify-content-between">
<span>Total:</span>
<span class="fw-bold text-primary" id="totalPrice">{{ product.formatted_price }}</span>
</div>
</div>
<!-- Buy Now Button -->
<button class="btn btn-success w-100 mb-2 buy-now-btn"
data-product-id="{{ product.product.id }}"
data-product-name="{{ product.product.name }}"
data-unit-price="{{ product.product.base_price }}"
data-category="{{ product.product.category_id }}"
data-provider-id="{{ product.product.provider_id }}"
data-provider-name="{{ product.product.provider_name }}"
title="Buy instantly with your wallet balance">
<i class="bi bi-lightning-charge me-2"></i>Buy Now
</button>
<!-- Add to Cart Button -->
<button class="btn btn-primary w-100 mb-2" id="addToCartBtn"
data-product-id="{{ product.product.id }}"
data-product-name="{{ product.product.name }}"
data-unit-price="{{ product.price.display_amount }}"
data-currency="{{ product.price.display_currency }}">
<i class="bi bi-cart-plus me-2"></i>Add to Cart
</button>
<!-- Quick Actions -->
<div class="d-grid gap-2">
<button class="btn btn-outline-secondary btn-sm">
<i class="bi bi-heart me-1"></i>Add to Wishlist
</button>
<button class="btn btn-outline-info btn-sm">
<i class="bi bi-share me-1"></i>Share Product
</button>
</div>
</div>
</div>
<!-- Provider Information -->
<div class="card mb-4">
<div class="card-body">
<h6 class="card-title">Provider Information</h6>
<div class="d-flex align-items-center mb-2">
<div class="provider-avatar me-3">
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 40px; height: 40px;">
{{ product.product.provider_name | slice(end=1) | upper }}
</div>
</div>
<div>
<div class="fw-bold">{{ product.product.provider_name }}</div>
{% if product.product.metadata.rating %}
<div class="text-muted small">
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star text-muted"></i>
<span class="ms-1">{{ product.product.metadata.rating }}/5 ({{ product.product.metadata.review_count }} reviews)</span>
</div>
{% endif %}
</div>
</div>
<p class="small text-muted mb-0">
Trusted provider in the ThreeFold ecosystem with verified infrastructure.
</p>
</div>
</div>
<!-- Currency Selector -->
<div class="card">
<div class="card-body">
<h6 class="card-title">Currency</h6>
<select class="form-select" id="currencySelector">
{% for currency in currencies %}
<option value="{{ currency.code }}"
{% if currency.code == user_currency %}selected{% endif %}>
{{ currency.symbol }} {{ currency.name }} ({{ currency.code }})
</option>
{% endfor %}
</select>
</div>
</div>
</div>
</div>
<!-- Recommendations Section -->
{% if recommendations and recommendations | length > 0 %}
<div class="mt-5">
<h4 class="mb-4">Recommended Products</h4>
<div class="row">
{% for rec in recommendations %}
<div class="col-lg-3 col-md-6 mb-4">
<div class="card h-100">
<div class="card-body">
<h6 class="card-title">
<a href="/products/{{ rec.product.id }}" class="text-decoration-none">
{{ rec.product.name }}
</a>
</h6>
<p class="card-text small text-muted">{{ rec.product.description | truncate(length=80) }}</p>
<div class="d-flex justify-content-between align-items-center">
<span class="text-primary fw-bold">{{ rec.formatted_price }}</span>
<div class="btn-group">
<button class="btn btn-success btn-sm buy-now-btn"
data-product-id="{{ rec.product.id }}"
data-product-name="{{ rec.product.name }}"
data-unit-price="{{ rec.price.display_amount }}"
data-currency="{{ rec.price.display_currency }}"
data-category="{{ rec.product.category_id }}">
<i class="bi bi-lightning-fill"></i>
</button>
<button class="btn btn-outline-primary btn-sm add-to-cart-btn"
data-product-id="{{ rec.product.id }}"
data-product-name="{{ rec.product.name }}"
data-unit-price="{{ rec.price.display_amount }}"
data-currency="{{ rec.price.display_currency }}">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
<style>
.spec-item {
padding: 0.5rem 0;
border-bottom: 1px solid #f0f0f0;
}
.spec-item:last-child {
border-bottom: none;
}
.price-display {
text-align: right;
}
.provider-avatar {
flex-shrink: 0;
}
</style>
<script src="/static/js/product-detail.js"></script>
{% endblock %}

View File

@@ -0,0 +1,74 @@
{% extends "marketplace/layout.html" %}
{% block title %}{{ product.product.name }} - Project Mycelium{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/marketplace">Marketplace</a></li>
<li class="breadcrumb-item">
<a href="/marketplace/{{ product.product.category_id }}">
{{ product.product.category_id | title }}
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">{{ product.product.name }}</li>
</ol>
</nav>
<div class="row">
<!-- Product Details -->
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<!-- Product Header -->
<div class="d-flex justify-content-between align-items-start mb-4">
<div>
<h1 class="h2 mb-2">{{ product.product.name }}</h1>
<div class="d-flex align-items-center mb-2">
<span class="badge bg-primary me-2">{{ product.product.category_id | title }}</span>
{% if product.product.metadata.featured %}
<span class="badge bg-warning me-2">Featured</span>
{% endif %}
<span class="badge bg-success">{{ product.product.availability }}</span>
</div>
<p class="text-muted mb-0">
<i class="bi bi-building me-1"></i>
{{ product.product.provider_name }}
{% if product.product.metadata.location %}
<span class="ms-3">
<i class="bi bi-geo-alt me-1"></i>
{{ product.product.metadata.location }}
</span>
{% endif %}
</p>
</div>
<div class="text-end">
<div class="price-display">
<div class="h3 text-primary mb-0">{{ product.formatted_price }}</div>
</div>
</div>
</div>
<!-- Product Description -->
<div class="mb-4">
<h5>Description</h5>
<p>{{ product.product.description }}</p>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Purchase Options</h5>
<button class="btn btn-primary w-100">Add to Cart</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,120 @@
{% extends "marketplace/layout.html" %}
{% block title %}{{ product.product.name }} - Project Mycelium{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/marketplace">Marketplace</a></li>
<li class="breadcrumb-item">
<a href="/marketplace/{{ product.product.category_id }}">
{{ product.product.category_id | title }}
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">{{ product.product.name }}</li>
</ol>
</nav>
<div class="row">
<!-- Product Details -->
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<!-- Product Header -->
<div class="d-flex justify-content-between align-items-start mb-4">
<div>
<h1 class="h2 mb-2">{{ product.product.name }}</h1>
<div class="d-flex align-items-center mb-2">
<span class="badge bg-primary me-2">{{ product.product.category_id | title }}</span>
{% if product.product.metadata.featured %}
<span class="badge bg-warning me-2">Featured</span>
{% endif %}
<span class="badge bg-success">{{ product.product.availability }}</span>
</div>
<p class="text-muted mb-0">
<i class="bi bi-building me-1"></i>
{{ product.product.provider_name }}
{% if product.product.metadata.location %}
<span class="ms-3">
<i class="bi bi-geo-alt me-1"></i>
{{ product.product.metadata.location }}
</span>
{% endif %}
</p>
</div>
<div class="text-end">
<div class="price-display">
<div class="h3 text-primary mb-0">{{ product.formatted_price }}</div>
</div>
</div>
</div>
<!-- Product Description -->
<div class="mb-4">
<h5>Description</h5>
<p>{{ product.product.description }}</p>
</div>
<!-- Product Specifications -->
{% if product.product.attributes %}
<div class="mb-4">
<h5>Specifications</h5>
<div class="row">
{% for key, attr in product.product.attributes %}
<div class="col-md-6 mb-3">
<div class="spec-item">
<strong>{{ key | replace(from="_", to=" ") | title }}:</strong>
<span class="ms-2">{{ attr.value }}</span>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Add to Cart Card -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Purchase Options</h5>
<!-- Quantity Selector -->
<div class="mb-3">
<label for="quantity" class="form-label">Quantity</label>
<div class="input-group">
<button class="btn btn-outline-secondary" type="button" id="decreaseQty">-</button>
<input type="number" class="form-control text-center" id="quantity" value="1" min="1" max="10">
<button class="btn btn-outline-secondary" type="button" id="increaseQty">+</button>
</div>
</div>
<!-- Total Price Display -->
<div class="mb-3">
<div class="d-flex justify-content-between">
<span>Total:</span>
<span class="fw-bold text-primary" id="totalPrice">{{ product.formatted_price }}</span>
</div>
</div>
<!-- Add to Cart Button -->
<button class="btn btn-primary w-100 mb-2" id="addToCartBtn"
data-product-id="{{ product.product.id }}"
data-product-name="{{ product.product.name }}"
data-unit-price="{{ product.price.display_amount }}"
data-currency="{{ product.price.display_currency }}">
<i class="bi bi-cart-plus me-2"></i>Add to Cart
</button>
</div>
</div>
</div>
</div>
</div>
<script src="/static/js/product-detail-step2.js"></script>
{% endblock %}

View File

@@ -0,0 +1,337 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Products{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1>All Products</h1>
<p class="lead mb-0">
{% if search_query.q %}
Search results for "{{ search_query.q }}"
{% elif search_query.category %}
{{ search_query.category | title }} Products
{% else %}
Browse all available products and services
{% endif %}
</p>
</div>
<div class="text-muted">
{% if response.total_count > 0 %}
Showing {{ response.products | length }} of {{ response.total_count }} products
{% endif %}
</div>
</div>
<!-- Search and Filter Section -->
<div class="card mb-4">
<div class="card-body">
<form method="GET" class="row g-3">
<!-- Search Query -->
<div class="col-md-4">
<label for="searchQuery" class="form-label">Search Products</label>
<input type="text" class="form-control" id="searchQuery" name="q"
value="{{ search_query.q or '' }}" placeholder="Search products...">
</div>
<!-- Category Filter -->
<div class="col-md-2">
<label for="categoryFilter" class="form-label">Category</label>
<select id="categoryFilter" name="category" class="form-select">
<option value="">All Categories</option>
{% for category in response.categories %}
<option value="{{ category.id }}"
{% if search_query.category == category.id %}selected{% endif %}>
{{ category.display_name }}
</option>
{% endfor %}
</select>
</div>
<!-- Price Range -->
<div class="col-md-2">
<label for="minPrice" class="form-label">Min Price</label>
<input type="number" class="form-control" id="minPrice" name="min_price"
value="{{ search_query.min_price or '' }}" placeholder="0">
</div>
<div class="col-md-2">
<label for="maxPrice" class="form-label">Max Price</label>
<input type="number" class="form-control" id="maxPrice" name="max_price"
value="{{ search_query.max_price or '' }}" placeholder="1000">
</div>
<!-- Provider Filter -->
<div class="col-md-2">
<label for="providerFilter" class="form-label">Provider</label>
<input type="text" class="form-control" id="providerFilter" name="provider"
value="{{ search_query.provider or '' }}" placeholder="Provider name">
</div>
<!-- Search Button -->
<div class="col-md-12 d-flex justify-content-between align-items-end">
<button type="submit" class="btn btn-primary">
<i class="bi bi-search me-1"></i>Search
</button>
<a href="/products" class="btn btn-outline-secondary">Clear Filters</a>
</div>
</form>
</div>
</div>
<!-- Results Section -->
{% if response.products and response.products | length > 0 %}
<!-- Sort Options -->
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group" role="group" aria-label="View options">
<input type="radio" class="btn-check" name="view-mode" id="grid-view" checked>
<label class="btn btn-outline-secondary" for="grid-view">
<i class="bi bi-grid-3x3-gap"></i> Grid
</label>
<input type="radio" class="btn-check" name="view-mode" id="list-view">
<label class="btn btn-outline-secondary" for="list-view">
<i class="bi bi-list"></i> List
</label>
</div>
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
Sort by: Price (Low to High)
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Price (Low to High)</a></li>
<li><a class="dropdown-item" href="#">Price (High to Low)</a></li>
<li><a class="dropdown-item" href="#">Name (A-Z)</a></li>
<li><a class="dropdown-item" href="#">Provider</a></li>
<li><a class="dropdown-item" href="#">Featured First</a></li>
</ul>
</div>
</div>
<!-- Products Grid -->
<div id="products-grid" class="row">
{% for product_data in response.products %}
<div class="col-lg-4 col-md-6 mb-4">
<div class="card h-100 product-card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title">
<a href="/products/{{ product_data.product.id }}" class="text-decoration-none text-dark">
{{ product_data.product.name }}
</a>
</h5>
{% if product_data.product.metadata.featured %}
<span class="badge bg-warning">Featured</span>
{% elif product_data.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product_data.product.availability }}</span>
{% endif %}
</div>
<div class="mb-2">
<span class="badge bg-primary">{{ product_data.product.category_id | title }}</span>
{% if product_data.product.metadata.location %}
<span class="badge bg-light text-dark ms-1">{{ product_data.product.metadata.location }}</span>
{% endif %}
</div>
<p class="card-text text-muted small">{{ product_data.product.description | truncate(length=100) }}</p>
<!-- Key Specifications -->
{% if product_data.product.attributes %}
<div class="mb-3">
<div class="row text-center">
{% if product_data.product.attributes.cpu_cores %}
<div class="col-4">
<div class="spec-badge">
<i class="bi bi-cpu text-primary"></i>
<div class="small">{{ product_data.product.attributes.cpu_cores.value }} cores</div>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.memory_gb %}
<div class="col-4">
<div class="spec-badge">
<i class="bi bi-memory text-primary"></i>
<div class="small">{{ product_data.product.attributes.memory_gb.value }} GB</div>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.storage_gb %}
<div class="col-4">
<div class="spec-badge">
<i class="bi bi-hdd text-primary"></i>
<div class="small">{{ product_data.product.attributes.storage_gb.value }} GB</div>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<div class="text-muted small mb-3">
<i class="bi bi-building me-1"></i>{{ product_data.product.provider_name }}
</div>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-center">
<div class="price-info">
<div class="fw-bold text-primary">{{ product_data.price.formatted_display }}</div>
{% if product_data.price.display_currency != "USD" %}
<div class="small text-muted">≈ ${{ product_data.product.base_price }}</div>
{% endif %}
</div>
<div class="btn-group">
<button class="btn btn-success btn-sm buy-now-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-category="{{ product_data.product.category_id }}"
data-unit-price="{{ product_data.product.base_price }}"
data-provider-id="{{ product_data.product.provider_id }}"
data-provider-name="{{ product_data.product.provider_name }}"
title="Buy instantly with your wallet balance">
<i class="bi bi-lightning-fill"></i>
</button>
<button class="btn btn-primary btn-sm add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus"></i>
</button>
<a href="/products/{{ product_data.product.id }}" class="btn btn-outline-primary btn-sm">View Details</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Products List View (Hidden by default) -->
<div id="products-list" class="d-none">
{% for product_data in response.products %}
<div class="card mb-3">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-8">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="mb-1">
<a href="/products/{{ product_data.product.id }}" class="text-decoration-none text-dark">
{{ product_data.product.name }}
</a>
</h5>
{% if product_data.product.metadata.featured %}
<span class="badge bg-warning">Featured</span>
{% elif product_data.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product_data.product.availability }}</span>
{% endif %}
</div>
<p class="text-muted mb-2">{{ product_data.product.description | truncate(length=150) }}</p>
<div class="text-muted small">
<span class="badge bg-primary me-2">{{ product_data.product.category_id | title }}</span>
<i class="bi bi-building me-1"></i>{{ product_data.product.provider_name }}
{% if product_data.product.metadata.location %}
<span class="ms-2">
<i class="bi bi-geo-alt me-1"></i>{{ product_data.product.metadata.location }}
</span>
{% endif %}
</div>
</div>
<div class="col-md-4 text-end">
<div class="fw-bold text-primary fs-5 mb-2">{{ product_data.price.formatted_display }}</div>
{% if product_data.price.display_currency != "USD" %}
<div class="small text-muted">≈ ${{ product_data.product.base_price }}</div>
{% endif %}
<div class="btn-group">
<button class="btn btn-primary btn-sm add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
<a href="/products/{{ product_data.product.id }}" class="btn btn-outline-primary btn-sm">View Details</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if response.total_pages > 1 %}
<nav aria-label="Product pages" class="mt-4">
<ul class="pagination justify-content-center">
{% if response.page > 0 %}
<li class="page-item">
<a class="page-link" href="?page={{ response.page - 1 }}{% if search_query.q %}&q={{ search_query.q }}{% endif %}{% if search_query.category %}&category={{ search_query.category }}{% endif %}">Previous</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Previous</span>
</li>
{% endif %}
{% for page_num in range(end=response.total_pages) %}
<li class="page-item {% if page_num == response.page %}active{% endif %}">
<a class="page-link" href="?page={{ page_num }}{% if search_query.q %}&q={{ search_query.q }}{% endif %}{% if search_query.category %}&category={{ search_query.category }}{% endif %}">{{ page_num + 1 }}</a>
</li>
{% endfor %}
{% if response.page < response.total_pages - 1 %}
<li class="page-item">
<a class="page-link" href="?page={{ response.page + 1 }}{% if search_query.q %}&q={{ search_query.q }}{% endif %}{% if search_query.category %}&category={{ search_query.category }}{% endif %}">Next</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Next</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<!-- No Results -->
<div class="text-center py-5">
<i class="bi bi-search display-1 text-muted"></i>
<h4 class="mt-3">No Products Found</h4>
{% if search_query.q or search_query.category %}
<p class="text-muted">Try adjusting your search criteria or browse all products.</p>
<a href="/products" class="btn btn-primary">Browse All Products</a>
{% else %}
<p class="text-muted">No products are currently available.</p>
{% endif %}
</div>
{% endif %}
</div>
<style>
.product-card {
transition: transform 0.2s ease-in-out;
}
.product-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.spec-badge {
padding: 0.5rem;
border: 1px solid #e9ecef;
border-radius: 0.375rem;
margin-bottom: 0.5rem;
}
.price-info {
text-align: left;
}
</style>
<script src="/static/js/products-page.js"></script>
{% endblock %}

View File

@@ -0,0 +1,358 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Professional Services{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>Professional Services</h1>
<p class="lead">Expert services to help you succeed with ThreeFold technology and infrastructure.</p>
<!-- Services Introduction -->
<div class="alert alert-info mb-4">
<div class="d-flex">
<div class="me-3">
<i class="bi bi-people-fill fs-3"></i>
</div>
<div>
<h5 class="alert-heading">Professional Support & Services</h5>
<p>Our certified experts and trusted community providers offer comprehensive services to help you deploy, manage, and optimize your ThreeFold infrastructure and applications.</p>
<hr>
<p class="mb-0">From initial consultation to ongoing support, we ensure your success with decentralized technology.</p>
</div>
</div>
</div>
<!-- Filter and Search Section -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Filter Services</h5>
<form class="row g-3" method="GET">
<div class="col-md-3">
<label for="serviceTypeFilter" class="form-label">Service Type</label>
<select name="service_type" id="serviceTypeFilter" class="form-select">
<option value="">All Services</option>
<option value="Consulting">Consulting</option>
<option value="Deployment">Deployment</option>
<option value="Support">Support</option>
<option value="Training">Training</option>
<option value="Development">Development</option>
<option value="Maintenance">Maintenance</option>
</select>
</div>
<div class="col-md-3">
<label for="expertiseFilter" class="form-label">Expertise Level</label>
<select name="expertise_level" id="expertiseFilter" class="form-select">
<option value="">Any</option>
<option value="Basic">Basic</option>
<option value="Intermediate">Intermediate</option>
<option value="Advanced">Advanced</option>
<option value="Expert">Expert</option>
</select>
</div>
<div class="col-md-3">
<label for="minPriceFilter" class="form-label">Min Price ($)</label>
<input type="number" name="min_price" id="minPriceFilter" class="form-control"
value="" placeholder="0" min="0" step="5">
</div>
<div class="col-md-3">
<label for="maxPriceFilter" class="form-label">Max Price ($)</label>
<input type="number" name="max_price" id="maxPriceFilter" class="form-control"
value="" placeholder="200" min="0" step="5">
</div>
<div class="col-md-6 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
<a href="/marketplace/services" class="btn btn-outline-secondary">Clear</a>
</div>
</form>
</div>
</div>
<!-- Services Grid -->
<div class="row" id="services-grid">
{% if service_products and service_products | length > 0 %}
{% for product_data in service_products %}
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<div class="service-icon me-3">
<i class="bi bi-gear-fill fs-2 text-primary"></i>
</div>
<div class="flex-grow-1">
<div class="d-flex justify-content-between align-items-start">
<h5 class="card-title mb-1">
<a href="/products/{{ product_data.product.id }}" class="text-decoration-none text-dark">
{{ product_data.product.name }}
</a>
</h5>
{% if product_data.product.metadata.featured %}
<span class="badge bg-warning">Featured</span>
{% elif product_data.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product_data.product.availability }}</span>
{% endif %}
</div>
<div class="text-muted small mb-2">
<i class="bi bi-building me-1"></i>{{ product_data.product.provider_name }}
{% if product_data.product.metadata.location %}
<span class="ms-2">
<i class="bi bi-geo-alt me-1"></i>{{ product_data.product.metadata.location }}
</span>
{% endif %}
</div>
</div>
</div>
<p class="card-text">{{ product_data.product.description | truncate(length=150) }}</p>
<!-- Service Details -->
{% if product_data.product.attributes %}
<div class="mb-3">
<h6 class="mb-2">Service Details:</h6>
<div class="row">
{% if product_data.product.attributes.duration_hours %}
<div class="col-md-6">
<div class="service-detail">
<i class="bi bi-clock me-2"></i>
<span>Duration: {{ product_data.product.attributes.duration_hours.value }} hours</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.expertise_level %}
<div class="col-md-6">
<div class="service-detail">
<i class="bi bi-star me-2"></i>
<span>Level: {{ product_data.product.attributes.expertise_level.value | title }}</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.response_time_hours %}
<div class="col-md-6">
<div class="service-detail">
<i class="bi bi-reply me-2"></i>
<span>Response: {{ product_data.product.attributes.response_time_hours.value }}h</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.support_type %}
<div class="col-md-6">
<div class="service-detail">
<i class="bi bi-headset me-2"></i>
<span>Type: {{ product_data.product.attributes.support_type.value | title }}</span>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Service Features/Tags -->
{% if product_data.product.metadata.tags and product_data.product.metadata.tags | length > 0 %}
<div class="mb-3">
<h6 class="mb-2">Includes:</h6>
<div class="d-flex flex-wrap">
{% for tag in product_data.product.metadata.tags %}
<span class="badge bg-light text-dark me-1 mb-1">{{ tag | title }}</span>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Provider Rating -->
{% if product_data.product.metadata.rating %}
<div class="mb-3">
<div class="d-flex align-items-center">
<span class="me-2">Rating:</span>
{% if product_data.product.metadata.rating >= 1 %}<i class="bi bi-star-fill text-warning"></i>{% else %}<i class="bi bi-star text-muted"></i>{% endif %}
{% if product_data.product.metadata.rating >= 2 %}<i class="bi bi-star-fill text-warning"></i>{% else %}<i class="bi bi-star text-muted"></i>{% endif %}
{% if product_data.product.metadata.rating >= 3 %}<i class="bi bi-star-fill text-warning"></i>{% else %}<i class="bi bi-star text-muted"></i>{% endif %}
{% if product_data.product.metadata.rating >= 4 %}<i class="bi bi-star-fill text-warning"></i>{% else %}<i class="bi bi-star text-muted"></i>{% endif %}
{% if product_data.product.metadata.rating >= 5 %}<i class="bi bi-star-fill text-warning"></i>{% else %}<i class="bi bi-star text-muted"></i>{% endif %}
<span class="ms-2 text-muted small">({{ product_data.product.metadata.review_count }} reviews)</span>
</div>
</div>
{% endif %}
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-center">
<div class="price-info">
<div class="fw-bold text-primary fs-5">{{ product_data.formatted_price }}</div>
<small class="text-muted">per engagement</small>
</div>
<div class="btn-group-vertical w-100">
<div class="btn-group mb-2">
<button class="btn btn-success btn-sm buy-now-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}"
data-category="services">
<i class="bi bi-lightning-charge me-1"></i>Buy Now
</button>
<button class="btn btn-primary btn-sm add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
</div>
<a href="/products/{{ product_data.product.id }}" class="btn btn-outline-primary btn-sm">View Details</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="text-center py-5">
<i class="bi bi-people display-1 text-muted"></i>
<h4 class="mt-3">No Services Available</h4>
<p class="text-muted">Check back later for new professional services.</p>
</div>
</div>
{% endif %}
</div>
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<nav aria-label="Service pages" class="mt-4">
<ul class="pagination justify-content-center">
<!-- Previous Page -->
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
{% if pagination.has_previous %}
<a class="page-link" href="?page={{ pagination.previous_page }}">Previous</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Previous</span>
{% endif %}
</li>
<!-- Page 1 -->
<li class="page-item {% if pagination.current_page == 0 %}active{% endif %}">
{% if pagination.current_page == 0 %}
<span class="page-link">1</span>
{% else %}
<a class="page-link" href="?page=0">1</a>
{% endif %}
</li>
<!-- Page 2 (if exists) -->
{% if pagination.total_pages > 1 %}
<li class="page-item {% if pagination.current_page == 1 %}active{% endif %}">
{% if pagination.current_page == 1 %}
<span class="page-link">2</span>
{% else %}
<a class="page-link" href="?page=1">2</a>
{% endif %}
</li>
{% endif %}
<!-- Page 3 (if exists) -->
{% if pagination.total_pages > 2 %}
<li class="page-item {% if pagination.current_page == 2 %}active{% endif %}">
{% if pagination.current_page == 2 %}
<span class="page-link">3</span>
{% else %}
<a class="page-link" href="?page=2">3</a>
{% endif %}
</li>
{% endif %}
<!-- Page 4 (if exists) -->
{% if pagination.total_pages > 3 %}
<li class="page-item {% if pagination.current_page == 3 %}active{% endif %}">
{% if pagination.current_page == 3 %}
<span class="page-link">4</span>
{% else %}
<a class="page-link" href="?page=3">4</a>
{% endif %}
</li>
{% endif %}
<!-- Page 5 (if exists) -->
{% if pagination.total_pages > 4 %}
<li class="page-item {% if pagination.current_page == 4 %}active{% endif %}">
{% if pagination.current_page == 4 %}
<span class="page-link">5</span>
{% else %}
<a class="page-link" href="?page=4">5</a>
{% endif %}
</li>
{% endif %}
<!-- Next Page -->
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
{% if pagination.has_next %}
<a class="page-link" href="?page={{ pagination.next_page }}">Next</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Next</span>
{% endif %}
</li>
</ul>
</nav>
<!-- Results Info -->
<div class="text-center text-muted mt-2">
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
({{ pagination.total_count }} total services)
</div>
{% endif %}
<!-- Service Categories -->
<div class="row mt-5">
<div class="col-12">
<h3 class="mb-4">Service Categories</h3>
</div>
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-lightbulb fs-1 text-primary mb-3"></i>
<h5>Consulting & Strategy</h5>
<p class="small text-muted">Architecture planning, technology assessment, and strategic guidance for your decentralized infrastructure.</p>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-tools fs-1 text-primary mb-3"></i>
<h5>Deployment & Setup</h5>
<p class="small text-muted">Professional deployment, configuration, and optimization of your ThreeFold infrastructure and applications.</p>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-headset fs-1 text-primary mb-3"></i>
<h5>Support & Maintenance</h5>
<p class="small text-muted">Ongoing support, monitoring, updates, and maintenance to keep your systems running smoothly.</p>
</div>
</div>
</div>
</div>
</div>
<style>
.service-detail {
margin-bottom: 8px;
font-size: 0.9rem;
}
.service-icon {
flex-shrink: 0;
}
.price-info {
text-align: left;
}
</style>
<!-- JSON hydration for services (CSP-safe) -->
<script type="application/json" id="services-data">{}</script>
<!-- External JS (CSP-compliant) -->
<script src="/static/js/services.js"></script>
{% endblock %}

View File

@@ -0,0 +1,447 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Rent Slice{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<div class="d-flex align-items-center mb-4">
<a href="/marketplace/compute" class="btn btn-outline-secondary me-3">
<i class="bi bi-arrow-left"></i> Back to Compute Resources
</a>
<h1 class="mb-0">Rent Compute Slice</h1>
</div>
{% if slice %}
<div class="row">
<!-- Slice Information -->
<div class="col-lg-4 mb-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Slice Details</h5>
</div>
<div class="card-body">
<div class="mb-3">
<strong>Node:</strong> {{ slice.node_id }}
</div>
<div class="mb-3">
<strong>Farmer:</strong> {{ slice.farmer_email }}
</div>
<div class="mb-3">
<strong>Specifications:</strong>
<ul class="list-unstyled mt-2">
<li><i class="bi bi-cpu me-2"></i>{{ slice.cpu_cores }} CPU Cores</li>
<li><i class="bi bi-memory me-2"></i>{{ slice.memory_gb }} GB RAM</li>
<li><i class="bi bi-hdd me-2"></i>{{ slice.storage_gb }} GB Storage</li>
</ul>
</div>
<div class="mb-3">
<strong>Price:</strong> ${{ slice.price_per_hour }}/hour
</div>
<div class="mb-3">
<strong>Available:</strong>
<span class="badge bg-success">{{ slice.available_quantity }} slices</span>
</div>
</div>
</div>
</div>
<!-- Rental Configuration Form -->
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Deployment Configuration</h5>
</div>
<div class="card-body">
<form id="sliceRentalForm" method="POST" action="/marketplace/slice/rent">
<!-- Hidden fields -->
<input type="hidden" name="farmer_email" value="{{ farmer_email }}">
<input type="hidden" name="node_id" value="{{ node_id }}">
<input type="hidden" name="combination_id" value="{{ combination_id }}">
<!-- Basic Configuration -->
<div class="row mb-4">
<div class="col-md-6">
<label for="quantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="quantity" name="quantity"
min="1" max="{{ slice.available_quantity }}" value="1" required>
<div class="form-text">Number of slices to rent</div>
</div>
<div class="col-md-6">
<label for="rental_duration_hours" class="form-label">Duration (Hours)</label>
<select class="form-select" id="rental_duration_hours" name="rental_duration_hours" required>
<option value="24">1 Day (24 hours)</option>
<option value="168">1 Week (168 hours)</option>
<option value="720" selected>1 Month (720 hours)</option>
<option value="2160">3 Months (2160 hours)</option>
<option value="4320">6 Months (4320 hours)</option>
<option value="8760">1 Year (8760 hours)</option>
</select>
</div>
</div>
<!-- Deployment Type Selection -->
<div class="mb-4">
<label class="form-label">Deployment Type</label>
<div class="row">
<div class="col-md-6">
<div class="card deployment-option" data-deployment="vm">
<div class="card-body text-center">
<input type="radio" class="btn-check" name="deployment_type" id="vm_deployment" value="vm" checked>
<label class="btn btn-outline-primary w-100" for="vm_deployment">
<i class="bi bi-pc-display-horizontal display-6 d-block mb-2"></i>
<h5>Virtual Machine</h5>
<p class="text-muted small mb-0">Deploy a single VM with your chosen OS</p>
</label>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card deployment-option" data-deployment="kubernetes">
<div class="card-body text-center">
<input type="radio" class="btn-check" name="deployment_type" id="k8s_deployment" value="kubernetes">
<label class="btn btn-outline-primary w-100" for="k8s_deployment">
<i class="bi bi-diagram-3 display-6 d-block mb-2"></i>
<h5>Kubernetes Cluster</h5>
<p class="text-muted small mb-0">Deploy a K8s cluster with configurable nodes</p>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- VM Configuration -->
<div id="vm_config" class="deployment-config">
<h6 class="mb-3">VM Configuration</h6>
<div class="row mb-3">
<div class="col-md-6">
<label for="vm_name" class="form-label">VM Name</label>
<input type="text" class="form-control" id="vm_name" name="vm_name"
placeholder="my-vm" pattern="[a-zA-Z0-9-]+" required>
<div class="form-text">Alphanumeric characters and hyphens only</div>
</div>
<div class="col-md-6">
<label for="vm_os" class="form-label">Operating System</label>
<select class="form-select" id="vm_os" name="vm_os">
<option value="ubuntu" selected>Ubuntu 22.04 LTS</option>
<option value="debian">Debian 12</option>
<option value="centos">CentOS Stream 9</option>
<option value="alpine">Alpine Linux</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="vm_ssh_key" class="form-label">SSH Public Key (Optional)</label>
<textarea class="form-control" id="vm_ssh_key" name="vm_ssh_key" rows="3"
placeholder="ssh-rsa AAAAB3NzaC1yc2E..."></textarea>
<div class="form-text">Paste your SSH public key for secure access</div>
</div>
</div>
<!-- Kubernetes Configuration -->
<div id="k8s_config" class="deployment-config" style="display: none;">
<h6 class="mb-3">Kubernetes Configuration</h6>
<div class="row mb-3">
<div class="col-md-6">
<label for="cluster_name" class="form-label">Cluster Name</label>
<input type="text" class="form-control" id="cluster_name" name="cluster_name"
placeholder="my-k8s-cluster" pattern="[a-zA-Z0-9-]+">
<div class="form-text">Alphanumeric characters and hyphens only</div>
</div>
<div class="col-md-6">
<label for="k8s_version" class="form-label">Kubernetes Version</label>
<select class="form-select" id="k8s_version" name="k8s_version">
<option value="1.29" selected>1.29 (Recommended)</option>
<option value="1.28">1.28</option>
<option value="1.30">1.30 (Latest)</option>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4">
<label for="k8s_masters" class="form-label">Master Nodes</label>
<select class="form-select" id="k8s_masters" name="k8s_masters">
<option value="1" selected>1 Master</option>
<option value="3">3 Masters (HA)</option>
</select>
</div>
<div class="col-md-4">
<label for="k8s_workers" class="form-label">Worker Nodes</label>
<select class="form-select" id="k8s_workers" name="k8s_workers">
<option value="1" selected>1 Worker</option>
<option value="2">2 Workers</option>
<option value="3">3 Workers</option>
<option value="5">5 Workers</option>
</select>
</div>
<div class="col-md-4">
<label for="k8s_network_plugin" class="form-label">Network Plugin</label>
<select class="form-select" id="k8s_network_plugin" name="k8s_network_plugin">
<option value="flannel" selected>Flannel</option>
<option value="calico">Calico</option>
<option value="weave">Weave Net</option>
</select>
</div>
</div>
</div>
<!-- Additional Options -->
<div class="mb-4">
<h6 class="mb-3">Additional Options</h6>
<div class="row">
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="auto_scaling" name="auto_scaling">
<label class="form-check-label" for="auto_scaling">
Auto Scaling
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="backup_enabled" name="backup_enabled">
<label class="form-check-label" for="backup_enabled">
Enable Backups
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="monitoring_enabled" name="monitoring_enabled" checked>
<label class="form-check-label" for="monitoring_enabled">
Enable Monitoring
</label>
</div>
</div>
</div>
</div>
<!-- Cost Calculation -->
<div class="card bg-light mb-4">
<div class="card-body">
<h6 class="mb-3">Cost Calculation</h6>
<div class="row">
<div class="col-md-6">
<div class="d-flex justify-content-between">
<span>Base Price per Hour:</span>
<span id="base_price">${{ slice.price_per_hour }}</span>
</div>
<div class="d-flex justify-content-between">
<span>Quantity:</span>
<span id="quantity_display">1</span>
</div>
<div class="d-flex justify-content-between">
<span>Duration:</span>
<span id="duration_display">720 hours</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex justify-content-between">
<span>Subtotal:</span>
<span id="subtotal">${{ slice.price_per_hour * 720 }}</span>
</div>
<div class="d-flex justify-content-between">
<span>K8s Multiplier:</span>
<span id="k8s_multiplier">1x</span>
</div>
<hr>
<div class="d-flex justify-content-between fw-bold">
<span>Total Cost:</span>
<span id="total_cost">${{ slice.price_per_hour * 720 }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Submit Button -->
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg" id="submitBtn">
<i class="bi bi-rocket me-2"></i>Deploy Now
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-exclamation-triangle display-1 text-warning"></i>
<h4 class="mt-3">Slice Not Found</h4>
<p class="text-muted">The requested slice combination could not be found.</p>
<a href="/marketplace/compute" class="btn btn-primary">Browse Available Slices</a>
</div>
{% endif %}
</div>
<!-- Pass data to JavaScript -->
<script type="application/json" id="slice-data">
{
"basePrice": {% if slice %}{{ slice.price_per_hour }}{% else %}0{% endif %},
"availableQuantity": {% if slice %}{{ slice.available_quantity }}{% else %}1{% endif %}
}
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const vmRadio = document.getElementById('vm_deployment');
const k8sRadio = document.getElementById('k8s_deployment');
const vmConfig = document.getElementById('vm_config');
const k8sConfig = document.getElementById('k8s_config');
const form = document.getElementById('sliceRentalForm');
// Get data from JSON script tag
const sliceData = JSON.parse(document.getElementById('slice-data').textContent);
const basePrice = sliceData.basePrice;
// Toggle deployment configuration
function toggleDeploymentConfig() {
if (vmRadio.checked) {
vmConfig.style.display = 'block';
k8sConfig.style.display = 'none';
// Make VM fields required
document.getElementById('vm_name').required = true;
document.getElementById('cluster_name').required = false;
} else {
vmConfig.style.display = 'none';
k8sConfig.style.display = 'block';
// Make K8s fields required
document.getElementById('vm_name').required = false;
document.getElementById('cluster_name').required = true;
}
updateCostCalculation();
}
// Update cost calculation
function updateCostCalculation() {
const quantity = parseInt(document.getElementById('quantity').value) || 1;
const duration = parseInt(document.getElementById('rental_duration_hours').value) || 720;
const masters = parseInt(document.getElementById('k8s_masters').value) || 1;
const workers = parseInt(document.getElementById('k8s_workers').value) || 1;
let multiplier = 1;
if (k8sRadio.checked) {
multiplier = masters + workers;
}
const subtotal = basePrice * quantity * duration;
const total = subtotal * multiplier;
document.getElementById('quantity_display').textContent = quantity;
document.getElementById('duration_display').textContent = duration + ' hours';
document.getElementById('subtotal').textContent = '$' + subtotal.toFixed(2);
document.getElementById('k8s_multiplier').textContent = multiplier + 'x';
document.getElementById('total_cost').textContent = '$' + total.toFixed(2);
}
// Event listeners
vmRadio.addEventListener('change', toggleDeploymentConfig);
k8sRadio.addEventListener('change', toggleDeploymentConfig);
document.getElementById('quantity').addEventListener('input', updateCostCalculation);
document.getElementById('rental_duration_hours').addEventListener('change', updateCostCalculation);
document.getElementById('k8s_masters').addEventListener('change', updateCostCalculation);
document.getElementById('k8s_workers').addEventListener('change', updateCostCalculation);
// Form submission
form.addEventListener('submit', function(e) {
e.preventDefault();
const submitBtn = document.getElementById('submitBtn');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = '<i class="bi bi-hourglass-split me-2"></i>Deploying...';
submitBtn.disabled = true;
const formData = new FormData(form);
window.apiJson('/marketplace/slice/rent', {
method: 'POST',
body: formData
})
.then(response => response)
.then(data => {
if (data.success) {
// Show success message
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success alert-dismissible fade show';
alertDiv.innerHTML = `
<i class="bi bi-check-circle me-2"></i>
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
form.parentNode.insertBefore(alertDiv, form);
// Redirect after 3 seconds
setTimeout(() => {
if (data.redirect_url) {
window.location.href = data.redirect_url;
}
}, 3000);
} else {
// Show error message
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
alertDiv.innerHTML = `
<i class="bi bi-exclamation-triangle me-2"></i>
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
form.parentNode.insertBefore(alertDiv, form);
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
})
.catch(error => {
console.error('Error:', error);
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
alertDiv.innerHTML = `
<i class="bi bi-exclamation-triangle me-2"></i>
An error occurred while processing your request.
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
form.parentNode.insertBefore(alertDiv, form);
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
});
});
// Initialize
toggleDeploymentConfig();
updateCostCalculation();
});
</script>
<style>
.deployment-option {
transition: all 0.3s ease;
cursor: pointer;
}
.deployment-option:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.deployment-config {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1rem;
background-color: #f8f9fa;
}
.btn-check:checked + .btn {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
color: white;
}
.spec-item {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
}
</style>
{% endblock %}

View File

@@ -0,0 +1,249 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - Statistics{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>Marketplace Statistics</h1>
<p class="lead">Analytics and utilization statistics for the Project Mycelium</p>
<!-- Overview Stats -->
<div class="row mt-4">
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Resource Distribution</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="resourceDistributionChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Monthly Growth</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="monthlyGrowthChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
<!-- Compute Resources Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>Compute Resources Statistics</h3>
<div class="row">
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>CPU Utilization by Region</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="cpuUtilizationChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Memory Allocation</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="memoryAllocationChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Storage Distribution</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="storageDistributionChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Resource Pricing Trends</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="resourcePricingChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 3Nodes Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>3Nodes Statistics</h3>
<div class="row">
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Geographic Distribution</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="nodeGeographicChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Node Types Distribution</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="nodeTypesChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Uptime Performance</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="nodeUptimeChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>3Node Certification Rate</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="nodeCertificationChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Gateways Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>Gateways Statistics</h3>
<div class="row">
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Gateway Traffic</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="gatewayTrafficChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Gateway Availability</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="gatewayAvailabilityChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Applications Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>Applications Statistics</h3>
<div class="row">
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Application Categories</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="appCategoriesChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Deployment Trends</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="appDeploymentChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Services Section -->
<div class="row mt-4">
<div class="col-12">
<div class="dashboard-section">
<h3>Services Statistics</h3>
<div class="row">
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Service Categories</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="serviceCategoriesChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-4 h-100">
<div class="card-header">
<h5>Average Hourly Rates</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<canvas id="serviceRatesChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
<style>
/* Ensure charts have consistent sizes */
.card.h-100 {
height: 400px !important;
}
.card-body {
height: 340px;
}
</style>
<script type="application/json" id="statistics-data">
{
}
</script>
<script src="/static/js/statistics.js"></script>
{% endblock %}

View File

@@ -0,0 +1,383 @@
{% extends "marketplace/layout.html" %}
{% block title %}Project Mycelium - 3Nodes Hardware{% endblock %}
{% block marketplace_content %}
<div class="my-4">
<h1>3Nodes Hardware</h1>
<p class="lead">Discover certified hardware nodes that power the ThreeFold Grid infrastructure.</p>
<!-- 3Nodes Introduction -->
<div class="alert alert-info mb-4">
<div class="d-flex">
<div class="me-3">
<i class="bi bi-server fs-3"></i>
</div>
<div>
<h5 class="alert-heading">What are 3Nodes?</h5>
<p>3Nodes are the physical hardware units that make up the ThreeFold Grid. These certified servers provide compute, storage, and network capacity to the decentralized internet infrastructure.</p>
<hr>
<p class="mb-0">By purchasing a 3Node, you become a farmer in the ThreeFold ecosystem, earning TFT rewards while contributing to the decentralized internet.</p>
</div>
</div>
</div>
<!-- Filter and Search Section -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Filter 3Nodes</h5>
<form class="row g-3" method="GET">
<div class="col-md-3">
<label for="locationFilter" class="form-label">Location</label>
<select name="location" id="locationFilter" class="form-select">
<option value="">All Locations</option>
<option value="Belgium">Belgium</option>
<option value="Austria">Austria</option>
<option value="Switzerland">Switzerland</option>
<option value="Netherlands">Netherlands</option>
<option value="Germany">Germany</option>
<option value="France">France</option>
<option value="UK">UK</option>
<option value="Canada">Canada</option>
<option value="USA">USA</option>
<option value="Japan">Japan</option>
</select>
</div>
<div class="col-md-3">
<label for="certificationFilter" class="form-label">Certification</label>
<select name="certification_status" id="certificationFilter" class="form-select">
<option value="">All Types</option>
<option value="DIY">DIY Nodes</option>
<option value="Certified">Certified Nodes</option>
<option value="Titan">Titan Nodes</option>
</select>
</div>
<div class="col-md-3">
<label for="minPriceFilter" class="form-label">Min Price ($)</label>
<input type="number" name="min_price" id="minPriceFilter" class="form-control"
value="" placeholder="0" min="0" step="10">
</div>
<div class="col-md-3">
<label for="maxPriceFilter" class="form-label">Max Price ($)</label>
<input type="number" name="max_price" id="maxPriceFilter" class="form-control"
value="" placeholder="500" min="0" step="10">
</div>
<div class="col-md-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
<a href="/marketplace/3nodes" class="btn btn-outline-secondary">Clear</a>
</div>
</form>
</div>
</div>
<!-- 3Nodes Grid -->
<div class="row">
{% if hardware_products and hardware_products | length > 0 %}
{% for product_data in hardware_products %}
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<div class="node-icon me-3">
<i class="bi bi-server fs-2 text-primary"></i>
</div>
<div class="flex-grow-1">
<div class="d-flex justify-content-between align-items-start">
<h5 class="card-title mb-1">
<a href="/products/{{ product_data.product.id }}" class="text-decoration-none text-dark">
{{ product_data.product.name }}
</a>
</h5>
{% if product_data.product.metadata.featured %}
<span class="badge bg-warning">Featured</span>
{% elif product_data.product.availability == "Available" %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">{{ product_data.product.availability }}</span>
{% endif %}
</div>
<div class="text-muted small mb-2">
<i class="bi bi-building me-1"></i>{{ product_data.product.provider_name }}
{% if product_data.product.metadata.location %}
<span class="ms-2">
<i class="bi bi-geo-alt me-1"></i>{{ product_data.product.metadata.location }}
</span>
{% endif %}
</div>
</div>
</div>
<p class="card-text">{{ product_data.product.description | truncate(length=120) }}</p>
<!-- Hardware Specifications -->
{% if product_data.product.attributes %}
<div class="mb-3">
<h6 class="mb-2">Hardware Specifications:</h6>
<div class="row">
{% if product_data.product.attributes.cpu_cores %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-cpu me-2 text-primary"></i>
<span>{{ product_data.product.attributes.cpu_cores.value }} CPU Cores</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.memory_gb %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-memory me-2 text-primary"></i>
<span>{{ product_data.product.attributes.memory_gb.value }} GB RAM</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.storage_gb %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-hdd me-2 text-primary"></i>
<span>{{ product_data.product.attributes.storage_gb.value }} GB Storage</span>
</div>
</div>
{% endif %}
{% if product_data.product.attributes.bandwidth_mbps %}
<div class="col-md-6">
<div class="spec-item">
<i class="bi bi-speedometer2 me-2 text-primary"></i>
<span>{{ product_data.product.attributes.bandwidth_mbps.value }} Mbps Network</span>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Node Features -->
{% if product_data.product.metadata.tags and product_data.product.metadata.tags | length > 0 %}
<div class="mb-3">
<h6 class="mb-2">Features:</h6>
<div class="d-flex flex-wrap">
{% for tag in product_data.product.metadata.tags %}
<span class="badge bg-light text-dark me-1 mb-1">{{ tag | title }}</span>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Farming Potential -->
<div class="mb-3">
<div class="alert alert-success small">
<i class="bi bi-currency-dollar me-1"></i>
<strong>Farming Potential:</strong> Earn TFT rewards by contributing to the ThreeFold Grid
</div>
</div>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-center">
<div class="price-info">
<div class="fw-bold text-primary fs-5">{{ product_data.formatted_price }}</div>
<small class="text-muted">one-time purchase</small>
</div>
<div class="btn-group-vertical w-100">
<div class="btn-group mb-2">
<button class="btn btn-success btn-sm buy-now-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}"
data-category="3nodes">
<i class="bi bi-lightning-charge me-1"></i>Buy Now
</button>
<button class="btn btn-primary btn-sm add-to-cart-btn"
data-product-id="{{ product_data.product.id }}"
data-product-name="{{ product_data.product.name }}"
data-unit-price="{{ product_data.price.display_amount }}"
data-currency="{{ product_data.price.display_currency }}">
<i class="bi bi-cart-plus me-1"></i>Add to Cart
</button>
</div>
<a href="/products/{{ product_data.product.id }}" class="btn btn-outline-primary btn-sm">View Details</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-12">
<div class="text-center py-5">
<i class="bi bi-server display-1 text-muted"></i>
<h4 class="mt-3">No 3Nodes Available</h4>
<p class="text-muted">Check back later for new hardware offerings.</p>
</div>
</div>
{% endif %}
</div>
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<nav aria-label="3Node pages" class="mt-4">
<ul class="pagination justify-content-center">
<!-- Previous Page -->
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
{% if pagination.has_previous %}
<a class="page-link" href="?page={{ pagination.previous_page }}">Previous</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Previous</span>
{% endif %}
</li>
<!-- Page 1 -->
<li class="page-item {% if pagination.current_page == 0 %}active{% endif %}">
{% if pagination.current_page == 0 %}
<span class="page-link">1</span>
{% else %}
<a class="page-link" href="?page=0">1</a>
{% endif %}
</li>
<!-- Page 2 (if exists) -->
{% if pagination.total_pages > 1 %}
<li class="page-item {% if pagination.current_page == 1 %}active{% endif %}">
{% if pagination.current_page == 1 %}
<span class="page-link">2</span>
{% else %}
<a class="page-link" href="?page=1">2</a>
{% endif %}
</li>
{% endif %}
<!-- Page 3 (if exists) -->
{% if pagination.total_pages > 2 %}
<li class="page-item {% if pagination.current_page == 2 %}active{% endif %}">
{% if pagination.current_page == 2 %}
<span class="page-link">3</span>
{% else %}
<a class="page-link" href="?page=2">3</a>
{% endif %}
</li>
{% endif %}
<!-- Page 4 (if exists) -->
{% if pagination.total_pages > 3 %}
<li class="page-item {% if pagination.current_page == 3 %}active{% endif %}">
{% if pagination.current_page == 3 %}
<span class="page-link">4</span>
{% else %}
<a class="page-link" href="?page=3">4</a>
{% endif %}
</li>
{% endif %}
<!-- Page 5 (if exists) -->
{% if pagination.total_pages > 4 %}
<li class="page-item {% if pagination.current_page == 4 %}active{% endif %}">
{% if pagination.current_page == 4 %}
<span class="page-link">5</span>
{% else %}
<a class="page-link" href="?page=4">5</a>
{% endif %}
</li>
{% endif %}
<!-- Next Page -->
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
{% if pagination.has_next %}
<a class="page-link" href="?page={{ pagination.next_page }}">Next</a>
{% else %}
<span class="page-link" tabindex="-1" aria-disabled="true">Next</span>
{% endif %}
</li>
</ul>
</nav>
<!-- Results Info -->
<div class="text-center text-muted mt-2">
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
({{ pagination.total_count }} total 3Nodes)
</div>
{% endif %}
<!-- Farming Information -->
<div class="row mt-5">
<div class="col-12">
<div class="card">
<div class="card-body">
<h3 class="card-title">Become a ThreeFold Farmer</h3>
<div class="row">
<div class="col-md-6">
<h5>1. Purchase Your 3Node</h5>
<p>Choose from certified hardware options that meet ThreeFold Grid requirements.</p>
<h5>2. Connect to the Grid</h5>
<p>Boot your 3Node with Zero-OS and connect it to the ThreeFold Grid infrastructure.</p>
</div>
<div class="col-md-6">
<h5>3. Start Earning</h5>
<p>Your 3Node automatically provides capacity to the grid and earns TFT rewards based on utilization.</p>
<h5>4. Monitor & Maintain</h5>
<p>Use the ThreeFold Dashboard to monitor your node's performance and earnings.</p>
</div>
</div>
<div class="alert alert-info mt-3">
<i class="bi bi-info-circle me-2"></i>
<strong>ROI Potential:</strong> 3Node farmers typically see return on investment within 2-4 years, depending on grid utilization and TFT price.
</div>
</div>
</div>
</div>
</div>
<!-- Node Types -->
<div class="row mt-4">
<div class="col-12">
<h3 class="mb-4">3Node Types</h3>
</div>
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-tools fs-1 text-primary mb-3"></i>
<h5>DIY Nodes</h5>
<p class="small text-muted">Build your own 3Node using compatible hardware. Most cost-effective option for technical users.</p>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-award fs-1 text-primary mb-3"></i>
<h5>Certified Nodes</h5>
<p class="small text-muted">Pre-built and certified hardware that's guaranteed to work with the ThreeFold Grid.</p>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-lightning fs-1 text-primary mb-3"></i>
<h5>Titan Nodes</h5>
<p class="small text-muted">High-performance enterprise-grade nodes for maximum farming potential and grid contribution.</p>
</div>
</div>
</div>
</div>
</div>
<style>
.spec-item {
margin-bottom: 8px;
font-size: 0.9rem;
}
.node-icon {
flex-shrink: 0;
}
.price-info {
text-align: left;
}
</style>
<script src="/static/js/marketplace-category.js"></script>
{% endblock %}