Antarmuka Pengguna (Frontend)

Serial Bagian 4: Membangun Dashboard sederhana untuk mengonsumsi API Machine Learning yang telah kita buat.

14. Struktur Folder Frontend

Ingat di Bagian 1 kita sudah membuat folder public. Sekarang kita isi dengan file statis. Kita menggunakan teknologi standar web (HTML, CSS, JS) tanpa framework berat agar ringan dan mudah di-deploy.

Terminal Command (SSH)
# Pastikan kita berada di folder public
cd /www/wwwroot/ao.baktimakmur.com

# Buat struktur folder
mkdir -p static/{css,js,images} uploads

Struktur akhir folder public akan terlihat seperti ini:

/www/wwwroot/ao.baktimakmur.com/
├── index.html          <-- Halaman Utama
├── uploads/             <-- Tempat upload file (jika ada)
└── static/
    ├── css/
    │   └── style.css    <-- Stylesheet
    └── js/
        └── app.js        <-- Logika API

15. Dashboard HTML

Halaman HTML ini berisi form untuk memasukkan data dan area untuk menampilkan hasil prediksi.

File: /www/wwwroot/ao.baktimakmur.com/index.html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AO AI Predictor Dashboard</title>
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>AO AI SaaS</h1>
            <p>Sistem Prediksi Cerdas</p>
        </header>

        <main>
            <section class="card">
                <h2>Input Prediksi</h2>
                <form id="predictionForm">
                    <!-- Input dinamis bisa ditambahkan di sini -->
                    <div class="form-group">
                        <label for="features">Data Input (Fitur)</label>
                        <!-- Contoh input array angka dipisahkan koma -->
                        <input type="text" id="features" name="features" placeholder="Contoh: 1.5, 2.3, 0.5, 1.0" required>
                        <small>Masukkan angka dipisahkan oleh koma</small>
                    </div>

                    <button type="submit" id="submitBtn">Prediksi Sekarang</button>
                </form>
            </section>

            <section class="card" id="resultSection" style="display: none;">
                <h2>Hasil Prediksi</h2>
                <div class="result-box">
                    <p>Status: <span id="status" class="badge">--</span></p>
                    <p>Nilai: <strong id="predictionValue">--</strong></p>
                    <p>Waktu: <span id="timestamp">--</span></p>
                </div>
            </section>
        </main>

        <footer>
            <p>&copy; 2024 AO AI Solutions. Production Environment.</p>
        </footer>
    </div>

    <script src="/static/js/app.js"></script>
</body>
</html>

16. Fetch API Logic (JavaScript)

File ini bertugas mengambil data dari form, mengirimnya ke endpoint Python API (`/api/v1/predict`), dan menampilkan hasilnya kembali ke layar pengguna.

File: /www/wwwroot/ao.baktimakmur.com/static/js/app.js
// Konfigurasi
const API_URL = '/api/v1/predict';
const API_KEY = 'prod-sk-88219238-x98'; // HARUS DIGANTI DENGAN API KEY USER YANG VALID DARI DB

document.getElementById('predictionForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const submitBtn = document.getElementById('submitBtn');
    const resultSection = document.getElementById('resultSection');
    const statusEl = document.getElementById('status');
    const valueEl = document.getElementById('predictionValue');
    const timeEl = document.getElementById('timestamp');
    
    // 1. Ambil data input
    const featuresInput = document.getElementById('features').value;
    
    // Validasi sederhana (Pastikan format array)
    const featuresArray = featuresInput.split(',').map(item => parseFloat(item.trim()));
    
    if (featuresArray.some(isNaN)) {
        alert('Input harus berupa angka yang dipisahkan koma.');
        return;
    }

    // 2. Update UI ke state Loading
    submitBtn.disabled = true;
    submitBtn.textContent = 'Memproses...';
    resultSection.style.display = 'none';

    try {
        // 3. Kirim Request ke Backend Python
        const response = await fetch(API_URL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-API-KEY': API_KEY // Mengirim header autentikasi
            },
            body: JSON.stringify({
                features: featuresArray
            })
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.detail || 'Gagal melakukan prediksi');
        }

        const data = await response.json();

        // 4. Tampilkan Hasil
        statusEl.textContent = 'Selesai';
        statusEl.style.color = 'green';
        valueEl.textContent = 'Data tersimpan di Job ID: ' + data.id;
        timeEl.textContent = new Date(data.created_at).toLocaleString();
        
        resultSection.style.display = 'block';

    } catch (error) {
        console.error('Error:', error);
        alert('Terjadi kesalahan: ' + error.message);
        
        statusEl.textContent = 'Gagal';
        statusEl.style.color = 'red';
        valueEl.textContent = error.message;
        resultSection.style.display = 'block';
    } finally {
        // 5. Kembalikan tombol ke state normal
        submitBtn.disabled = false;
        submitBtn.textContent = 'Prediksi Sekarang';
    }
});
Info Keamanan: Dalam aplikasi Frontend murni (bukan SPA dengan Env vars), API Key terbaca jika user "Inspect Element". Untuk SaaS Production, sebaiknya API Request dilewatkan melalui Proxy di Server Anda sendiri agar API Key tidak terekspos. Namun, untuk contoh ini, kita gunakan header langsung.

17. Styling Dasar (CSS)

Agar tampilan tidak terlihat kaku dan sesuai tema SaaS Premium.

File: /www/wwwroot/ao.baktimakmur.com/static/css/style.css
:root {
    --primary: #8b5cf6;
    --bg-color: #f8fafc;
    --card-bg: #ffffff;
    --text-color: #1e293b;
}

body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
    background-color: var(--bg-color);
    color: var(--text-color);
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

.container {
    width: 100%;
    max-width: 500px;
    padding: 20px;
}

header {
    text-align: center;
    margin-bottom: 2rem;
}

header h1 {
    color: var(--primary);
    margin-bottom: 0.5rem;
}

.card {
    background: var(--card-bg);
    padding: 2rem;
    border-radius: 12px;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    margin-bottom: 1.5rem;
}

.form-group {
    margin-bottom: 1.5rem;
}

label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
    font-size: 0.9rem;
}

input[type="text"] {
    width: 100%;
    padding: 0.75rem;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    font-size: 1rem;
}

button {
    width: 100%;
    padding: 0.75rem;
    background-color: var(--primary);
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s;
}

button:hover {
    background-color: #7c3aed;
}

button:disabled {
    background-color: #cbd5e1;
    cursor: not-allowed;
}

.badge {
    font-weight: bold;
}

Final Integration Check

Semua file sudah ada di tempatnya. Sekarang lakukan pengecekan akhir di browser:

  1. Buka https://ao.baktimakmur.com (pastikan SSL sudah aktif di panel).
  2. Anda akan melihat form "Input Prediksi".
  3. Masukkan angka dummy (misal: 10, 20, 30, 40).
  4. Klik "Prediksi Sekarang".
  5. Buka tab Network di Developer Tools (F12) browser.
  6. Lihat request ke /api/v1/predict. Status harus 200 OK.
  7. Dashboard akan menampilkan Job ID dari proses yang baru dibuat.

Selamat! 🎉

Anda telah membangun full-stack sistem AI SaaS, mulai dari Database, Backend Python (FastAPI), Machine Learning Inference, Orchestration (Supervisor), Web Server (Nginx), hingga Frontend Dashboard yang interaktif.