Spaces:
Sleeping
Sleeping
| {% extends "base.html" %} | |
| {% block title %}{{ tutorial.title }} - Dify AI Learning{% endblock %} | |
| {% block extra_head %} | |
| <link rel="stylesheet" href="{{ url_for('static', filename='js/tutorial.js') }}"> | |
| {% endblock %} | |
| {% block content %} | |
| <div class="container py-4"> | |
| <div class="row"> | |
| <!-- Sidebar Navigation --> | |
| <div class="col-lg-3"> | |
| <div class="tutorial-sidebar sticky-top"> | |
| <div class="tutorial-progress-info mb-4"> | |
| <h6 class="text-muted">Tutorial Progress</h6> | |
| <div class="progress mb-2"> | |
| <div class="progress-bar" role="progressbar" | |
| style="width: {{ ((user_progress.current_step if user_progress else 0) / tutorial.steps|length * 100)|round }}%" | |
| id="tutorial-progress-bar"> | |
| </div> | |
| </div> | |
| <small class="text-muted"> | |
| Step <span id="current-step">{{ user_progress.current_step + 1 if user_progress else 1 }}</span> | |
| of {{ tutorial.steps|length }} | |
| </small> | |
| </div> | |
| <nav class="tutorial-nav"> | |
| <h6 class="text-muted mb-3">Steps</h6> | |
| <ul class="nav nav-pills flex-column" id="tutorial-steps-nav"> | |
| {% for step in tutorial.steps %} | |
| <li class="nav-item"> | |
| <a class="nav-link step-nav-link {% if loop.index0 == (user_progress.current_step if user_progress else 0) %}active{% endif %}" | |
| href="#step-{{ loop.index0 }}" | |
| data-step="{{ loop.index0 }}"> | |
| <span class="step-number">{{ loop.index }}</span> | |
| {{ step.title }} | |
| </a> | |
| </li> | |
| {% endfor %} | |
| </ul> | |
| </nav> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="col-lg-9"> | |
| <!-- Tutorial Header --> | |
| <div class="tutorial-header mb-4"> | |
| <nav aria-label="breadcrumb"> | |
| <ol class="breadcrumb"> | |
| <li class="breadcrumb-item"><a href="{{ url_for('index') }}">Home</a></li> | |
| <li class="breadcrumb-item active">{{ tutorial.title }}</li> | |
| </ol> | |
| </nav> | |
| <h1 class="tutorial-title">{{ tutorial.title }}</h1> | |
| <div class="tutorial-meta"> | |
| <span class="badge bg-{{ tutorial.difficulty_color }} me-2">{{ tutorial.difficulty }}</span> | |
| <span class="text-muted"> | |
| <i data-feather="clock" class="small me-1"></i> | |
| {{ tutorial.estimated_time }} minutes | |
| </span> | |
| <span class="text-muted ms-3"> | |
| <i data-feather="bookmark" class="small me-1"></i> | |
| {{ tutorial.category }} | |
| </span> | |
| </div> | |
| <p class="tutorial-description mt-3">{{ tutorial.description }}</p> | |
| </div> | |
| <!-- Tutorial Steps --> | |
| <div class="tutorial-content"> | |
| {% for step in tutorial.steps %} | |
| <div class="tutorial-step {% if loop.index0 == (user_progress.current_step if user_progress else 0) %}active{% endif %}" | |
| id="step-{{ loop.index0 }}" | |
| data-step="{{ loop.index0 }}"> | |
| <div class="step-header"> | |
| <h2 class="step-title"> | |
| <span class="step-number-badge">{{ loop.index }}</span> | |
| {{ step.title }} | |
| </h2> | |
| {% if step.estimated_time %} | |
| <span class="step-time"> | |
| <i data-feather="clock" class="small me-1"></i> | |
| {{ step.estimated_time }} min | |
| </span> | |
| {% endif %} | |
| </div> | |
| <div class="step-content"> | |
| {{ step.content|safe }} | |
| {% if step.code_example %} | |
| <div class="code-example mt-4"> | |
| <h6>Example:</h6> | |
| <pre><code>{{ step.code_example }}</code></pre> | |
| </div> | |
| {% endif %} | |
| {% if step.interactive_demo %} | |
| <div class="interactive-demo mt-4"> | |
| <h6>Interactive Demo:</h6> | |
| <div class="demo-container"> | |
| {{ step.interactive_demo|safe }} | |
| </div> | |
| </div> | |
| {% endif %} | |
| {% if step.tips %} | |
| <div class="alert alert-info mt-4"> | |
| <h6><i data-feather="lightbulb" class="me-2"></i>Tips:</h6> | |
| <ul class="mb-0"> | |
| {% for tip in step.tips %} | |
| <li>{{ tip }}</li> | |
| {% endfor %} | |
| </ul> | |
| </div> | |
| {% endif %} | |
| </div> | |
| <!-- Step Navigation --> | |
| <div class="step-navigation mt-4 pt-4 border-top"> | |
| <div class="d-flex justify-content-between"> | |
| <div> | |
| {% if loop.index0 > 0 %} | |
| <button class="btn btn-outline-secondary" | |
| onclick="navigateToStep({{ loop.index0 - 1 }})"> | |
| <i data-feather="chevron-left" class="me-1"></i> | |
| Previous | |
| </button> | |
| {% endif %} | |
| </div> | |
| <div> | |
| {% if loop.index0 < tutorial.steps|length - 1 %} | |
| <button class="btn btn-primary" | |
| onclick="navigateToStep({{ loop.index0 + 1 }})"> | |
| Next | |
| <i data-feather="chevron-right" class="ms-1"></i> | |
| </button> | |
| {% else %} | |
| <button class="btn btn-success" onclick="completeTutorial()"> | |
| <i data-feather="check" class="me-1"></i> | |
| Complete Tutorial | |
| </button> | |
| {% endif %} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tutorial completion modal --> | |
| <div class="modal fade" id="completionModal" tabindex="-1"> | |
| <div class="modal-dialog"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title"> | |
| <i data-feather="award" class="me-2"></i> | |
| Congratulations! | |
| </h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
| </div> | |
| <div class="modal-body"> | |
| <p>You've successfully completed <strong>{{ tutorial.title }}</strong>!</p> | |
| <p>You're ready to move on to the next tutorial or try a hands-on project.</p> | |
| </div> | |
| <div class="modal-footer"> | |
| <a href="{{ url_for('index') }}" class="btn btn-primary">Back to Home</a> | |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Continue Learning</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block extra_scripts %} | |
| <script src="{{ url_for('static', filename='js/tutorial.js') }}"></script> | |
| <script> | |
| </script> | |
| {% endblock %} | |