| {% extends "base.html" %} |
|
|
| {% block title %}Student Feedback Sentiment Analysis{% endblock %} |
| {% block page_title %}Student Feedback Analysis{% endblock %} |
|
|
| {% block content %} |
| <div class="row justify-content-center"> |
| <div class="col-lg-8"> |
|
|
| |
| <div class="card shadow-sm mb-4"> |
| <div class="card-body py-3"> |
| <div class="d-flex justify-content-center"> |
| <div class="btn-group" role="group" aria-label="Analysis mode"> |
| <input type="radio" class="btn-check" name="analysisMode" id="singleMode" value="single" checked> |
| <label class="btn btn-outline-secondary" for="singleMode"> |
| <i class="fas fa-keyboard me-1"></i>Nhập đơn lẻ |
| </label> |
| |
| <input type="radio" class="btn-check" name="analysisMode" id="csvMode" value="csv"> |
| <label class="btn btn-outline-secondary" for="csvMode"> |
| <i class="fas fa-file-csv me-1"></i>Upload CSV |
| </label> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="singleFeedbackForm" class="card shadow-lg"> |
| <div class="card-header bg-primary text-white"> |
| <h3 class="card-title mb-0"> |
| <i class="fas fa-comment-dots me-2"></i> |
| Nhập Feedback |
| </h3> |
| </div> |
| <div class="card-body"> |
| <form id="feedbackForm"> |
| <div class="mb-3"> |
| <label for="feedbackText" class="form-label fw-bold"> |
| <i class="fas fa-edit me-2"></i> |
| Feedback của bạn: |
| </label> |
| <textarea |
| class="form-control" |
| id="feedbackText" |
| rows="5" |
| placeholder="Nhập feedback về giảng viên, chương trình đào tạo, cơ sở vật chất..." |
| required |
| ></textarea> |
| <div class="form-text"> |
| <i class="fas fa-info-circle me-1"></i> |
| Nhập feedback bằng tiếng Việt để có kết quả chính xác nhất |
| </div> |
| </div> |
| <div class="d-grid"> |
| <button type="submit" class="btn btn-primary btn-lg" id="analyzeBtn"> |
| <i class="fas fa-search me-2"></i> |
| Phân Tích Feedback |
| </button> |
| </div> |
| </form> |
| </div> |
| </div> |
|
|
| |
| <div id="csvUploadForm" class="card shadow-lg" style="display: none;"> |
| <div class="card-header bg-primary text-white"> |
| <h3 class="card-title mb-0"> |
| <i class="fas fa-file-csv me-2"></i> |
| Upload File CSV |
| </h3> |
| </div> |
| <div class="card-body"> |
| <form id="csvForm" enctype="multipart/form-data"> |
| <div class="mb-3"> |
| <label for="csvFile" class="form-label fw-bold"> |
| <i class="fas fa-upload me-1"></i> |
| Chọn file CSV: |
| </label> |
| <input type="file" id="csvFile" class="form-control" accept=".csv" required> |
| <div class="form-text"> |
| <i class="fas fa-info-circle me-1"></i> |
| File CSV phải có cột chứa feedback (tên cột: 'feedback', 'text', 'content' hoặc 'comment') |
| </div> |
| <div class="form-text"> |
| <i class="fas fa-exclamation-triangle me-1 text-warning"></i> |
| <small>File phải được mã hóa UTF-8 và có header (tên cột)</small> |
| </div> |
| <div class="form-text"> |
| <i class="fas fa-download me-1 text-success"></i> |
| <a href="#" id="downloadTemplate" class="text-decoration-none"> |
| <small>Tải file CSV mẫu</small> |
| </a> |
| </div> |
| </div> |
| |
| <div class="mb-3"> |
| <div class="d-flex align-items-center"> |
| <i class="fas fa-exclamation-triangle me-2 text-warning"></i> |
| <small class="text-muted">Quá trình phân tích có thể mất vài phút tùy thuộc vào số lượng feedback</small> |
| </div> |
| </div> |
| |
| <div class="d-grid"> |
| <button type="submit" id="analyzeCsvBtn" class="btn btn-primary btn-lg"> |
| <i class="fas fa-chart-bar me-2"></i> |
| Phân Tích File CSV |
| </button> |
| </div> |
| </form> |
| </div> |
| </div> |
|
|
| |
| <div id="loadingSpinner" class="text-center mb-4" style="display: none;"> |
| <div class="spinner-border text-primary" role="status"> |
| <span class="visually-hidden">Loading...</span> |
| </div> |
| <p class="mt-2 text-muted">Đang phân tích feedback...</p> |
| </div> |
|
|
| |
| <div id="results" style="display: none;"> |
| |
| <div class="card shadow-lg mb-3"> |
| <div class="card-header bg-primary text-white"> |
| <h4 class="card-title mb-0"> |
| <i class="fas fa-heart me-2"></i> |
| Phân loại cảm xúc của feedback |
| </h4> |
| </div> |
| <div class="card-body"> |
| <div class="row align-items-center"> |
| <div class="col-md-3"> |
| <div id="sentimentIcon" class="text-center"> |
| <i class="fas fa-smile fa-3x"></i> |
| </div> |
| </div> |
| <div class="col-md-9"> |
| <h3 id="sentimentResult" class="mb-2"></h3> |
| <p id="sentimentDescription" class="text-muted mb-0"></p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="card shadow-lg mb-4"> |
| <div class="card-header bg-primary text-white"> |
| <h4 class="card-title mb-0"> |
| <i class="fas fa-tags me-2"></i> |
| Chủ đề liên quan đến nội dung feedback |
| </h4> |
| </div> |
| <div class="card-body"> |
| <div class="row align-items-center"> |
| <div class="col-md-3"> |
| <div id="topicIcon" class="text-center"> |
| <i class="fas fa-user-tie fa-3x"></i> |
| </div> |
| </div> |
| <div class="col-md-9"> |
| <h3 id="topicResult" class="mb-2"></h3> |
| <p id="topicDescription" class="text-muted mb-0"></p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="card shadow-sm"> |
| <div class="card-header bg-primary text-white"> |
| <h5 class="card-title mb-0"> |
| <i class="fas fa-quote-left me-2"></i> |
| Feedback Gốc |
| </h5> |
| </div> |
| <div class="card-body"> |
| <blockquote class="blockquote mb-0"> |
| <p id="originalText" class="mb-0"></p> |
| </blockquote> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="errorMessage" class="alert alert-danger" style="display: none;"> |
| <i class="fas fa-exclamation-triangle me-2"></i> |
| <span id="errorText"></span> |
| </div> |
|
|
| |
| <div id="timeFilter" class="card shadow-lg mt-4"> |
| <div class="card-header"> |
| <h5 class="card-title mb-0"> |
| <i class="fas fa-filter me-2"></i> |
| Lọc theo thời gian |
| </h5> |
| </div> |
| <div class="card-body"> |
| <div class="mb-3"> |
| <label class="form-label fw-bold"> |
| <i class="fas fa-clock me-1"></i> |
| Khoảng thời gian: |
| </label> |
| </div> |
| |
| <div class="row mb-3 g-2"> |
| <div class="col"> |
| <input type="radio" class="btn-check" name="timeFilter" id="allTime" value="all" checked> |
| <label class="btn btn-outline-secondary w-100 text-center d-flex flex-column align-items-center justify-content-center" for="allTime" style="height: 60px;"> |
| <i class="fas fa-globe mb-1"></i> |
| <small>Tất cả</small> |
| </label> |
| </div> |
| <div class="col"> |
| <input type="radio" class="btn-check" name="timeFilter" id="today" value="today"> |
| <label class="btn btn-outline-secondary w-100 text-center d-flex flex-column align-items-center justify-content-center" for="today" style="height: 60px;"> |
| <i class="fas fa-calendar-day mb-1"></i> |
| <small>Hôm nay</small> |
| </label> |
| </div> |
| <div class="col"> |
| <input type="radio" class="btn-check" name="timeFilter" id="lastWeek" value="week"> |
| <label class="btn btn-outline-secondary w-100 text-center d-flex flex-column align-items-center justify-content-center" for="lastWeek" style="height: 60px;"> |
| <i class="fas fa-calendar-week mb-1"></i> |
| <small>7 ngày trước</small> |
| </label> |
| </div> |
| <div class="col"> |
| <input type="radio" class="btn-check" name="timeFilter" id="lastMonth" value="month"> |
| <label class="btn btn-outline-secondary w-100 text-center d-flex flex-column align-items-center justify-content-center" for="lastMonth" style="height: 60px;"> |
| <i class="fas fa-calendar-alt mb-1"></i> |
| <small>30 ngày trước</small> |
| </label> |
| </div> |
| <div class="col"> |
| <input type="radio" class="btn-check" name="timeFilter" id="custom" value="custom"> |
| <label class="btn btn-outline-secondary w-100 text-center d-flex flex-column align-items-center justify-content-center" for="custom" style="height: 60px;"> |
| <i class="fas fa-calendar-check mb-1"></i> |
| <small>Tùy chọn</small> |
| </label> |
| </div> |
| </div> |
| |
| <div id="customDateRange" class="d-flex gap-2 mb-3" style="display: none !important;"> |
| <input type="date" id="startDate" class="form-control form-control-sm flex-fill" placeholder="Từ ngày"> |
| <input type="date" id="endDate" class="form-control form-control-sm flex-fill" placeholder="Đến ngày"> |
| </div> |
| |
| <div class="mt-3"> |
| <div class="d-flex align-items-center"> |
| <i class="fas fa-info-circle me-2"></i> |
| <span id="filterInfo" class="small">Hiển thị tất cả feedback</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="feedbackHistory" class="card shadow-lg mt-4"> |
| <div class="card-header"> |
| <h4 class="card-title mb-0 d-flex align-items-center"> |
| <i class="fas fa-history me-2"></i> |
| Lịch sử Feedback |
| <span id="feedbackCount" class="badge bg-light text-dark ms-2">0</span> |
| </h4> |
| </div> |
| <div class="card-body"> |
| <div id="historyLoading" class="text-center py-3" style="display: none;"> |
| <div class="spinner-border text-primary" role="status"> |
| <span class="visually-hidden">Loading...</span> |
| </div> |
| <p class="mt-2 text-muted">Đang tải lịch sử...</p> |
| </div> |
| <div id="historyContent"> |
| |
| </div> |
| <div id="historyPagination" class="d-flex justify-content-center mt-3"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| </div> |
| </div> |
| {% endblock %} |
|
|
| {% block extra_scripts %} |
| <script src="{{ url_for('static', filename='js/app.js') }}"></script> |
| <style> |
| |
| #timeFilter .btn { |
| transition: all 0.3s ease; |
| font-weight: 500; |
| min-height: 60px; |
| border-radius: 8px; |
| white-space: normal; |
| line-height: 1.2; |
| } |
| |
| #timeFilter .btn:hover { |
| background-color: var(--gray-100); |
| } |
| |
| #timeFilter .btn-check:checked + .btn { |
| background-color: var(--gray-400); |
| border-color: var(--gray-400); |
| color: #F3F4F6; |
| } |
| |
| #timeFilter .btn i { |
| font-size: 1.1rem; |
| } |
| |
| #timeFilter .btn small { |
| font-size: 0.75rem; |
| font-weight: 500; |
| } |
| |
| #timeFilter .form-control { |
| transition: all 0.3s ease; |
| } |
| |
| #timeFilter .form-control:focus { |
| border-color: var(--gray-400); |
| box-shadow: 0 0 0 0.2rem rgba(107, 114, 128, 0.25); |
| } |
| |
| #customDateRange { |
| display: none; |
| } |
| |
| #customDateRange.show { |
| display: flex; |
| } |
| |
| #feedbackHistory .card-body { |
| padding: 1.5rem; |
| } |
| |
| |
| #filterInfo { |
| transition: all 0.3s ease; |
| } |
| |
| |
| .card { |
| transition: all 0.3s ease; |
| } |
| |
| .card:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 8px 25px rgba(0,0,0,0.1) !important; |
| } |
| </style> |
| {% endblock %} |