π–ͺπ—ˆπ–½π–Ύ π–¨π—Œπ—‚ π–₯𝗂𝗅𝖾 𝗆𝖺𝗂𝗇.π—ƒπ—Œ

 π–ͺπ—ˆπ–½π–Ύ π–¨π—Œπ—‚ π–₯𝗂𝗅𝖾 π—†π–Ίπ—‚𝗇.π—ƒπ—Œ


π–‘π–Ίπ—‡π—π—Ž π–»π—Žπ–Ίπ—π—„π–Ίπ—‡ π—„π—ˆπ–½π–Ύ π—‚π—Œπ—‚ 𝖿𝗂𝗅𝖾 π—†π–Ίπ—‚𝗇.π—ƒπ—Œ

FarmerSmartAI - File JavaScript Utama


Berikut kode lengkap untuk file main.js yang berisi fungsi-fungsi umum dan utilitas untuk aplikasi FarmerSmartAI:


```javascript

// main.js - JavaScript Utama FarmerSmartAI


class FarmerSmartAI {

    constructor() {

        this.currentUser = null;

        this.settings = this.loadSettings();

        this.init();

    }


    // ===== INITIALIZATION =====

    init() {

        this.setupEventListeners();

        this.setupServiceWorker();

        this.setupOfflineDetection();

        this.loadUserData();

        this.setupTheme();

        this.setupLanguage();

    }


    // ===== USER MANAGEMENT =====

    async loadUserData() {

        try {

            const userData = localStorage.getItem('farmerSmartAI_user');

            if (userData) {

                this.currentUser = JSON.parse(userData);

                this.updateUIUserInfo();

            }

        } catch (error) {

            console.error('Error loading user data:', error);

        }

    }


    async login(email, password) {

        try {

            // Simulasi API call

            const response = await this.mockAPICall('login', { email, password });

            

            if (response.success) {

                this.currentUser = response.user;

                localStorage.setItem('farmerSmartAI_user', JSON.stringify(response.user));

                this.updateUIUserInfo();

                this.showNotification('Login berhasil!', 'success');

                return true;

            } else {

                this.showNotification('Email atau password salah', 'error');

                return false;

            }

        } catch (error) {

            console.error('Login error:', error);

            this.showNotification('Terjadi kesalahan saat login', 'error');

            return false;

        }

    }


    async register(userData) {

        try {

            const response = await this.mockAPICall('register', userData);

            

            if (response.success) {

                this.showNotification('Registrasi berhasil! Silakan login.', 'success');

                return true;

            } else {

                this.showNotification('Registrasi gagal', 'error');

                return false;

            }

        } catch (error) {

            console.error('Registration error:', error);

            this.showNotification('Terjadi kesalahan saat registrasi', 'error');

            return false;

        }

    }


    logout() {

        this.currentUser = null;

        localStorage.removeItem('farmerSmartAI_user');

        window.location.href = 'index.html';

    }


    updateUIUserInfo() {

        const userElements = document.querySelectorAll('.user-info, .user-avatar, .profile-info');

        

        userElements.forEach(element => {

            if (element.classList.contains('user-avatar') && this.currentUser) {

                const names = this.currentUser.name.split(' ');

                const initials = names.map(name => name[0]).join('').toUpperCase();

                element.textContent = initials;

            }

            

            if (element.classList.contains('profile-info') && this.currentUser) {

                const nameElement = element.querySelector('h3');

                const emailElement = element.querySelector('.profile-email');

                

                if (nameElement) nameElement.textContent = this.currentUser.name;

                if (emailElement) emailElement.textContent = this.currentUser.email;

            }

        });

    }


    // ===== SETTINGS MANAGEMENT =====

    loadSettings() {

        const defaultSettings = {

            theme: 'light',

            language: 'id',

            notifications: {

                email: true,

                browser: true,

                sms: false

            },

            units: {

                temperature: 'celsius',

                distance: 'metric',

                volume: 'liters'

            }

        };


        try {

            const savedSettings = localStorage.getItem('farmerSmartAI_settings');

            return savedSettings ? { ...defaultSettings, ...JSON.parse(savedSettings) } : defaultSettings;

        } catch (error) {

            console.error('Error loading settings:', error);

            return defaultSettings;

        }

    }


    saveSettings(newSettings) {

        this.settings = { ...this.settings, ...newSettings };

        localStorage.setItem('farmerSmartAI_settings', JSON.stringify(this.settings));

        this.applySettings();

    }


    applySettings() {

        this.applyTheme();

        this.applyLanguage();

    }


    // ===== THEME MANAGEMENT =====

    setupTheme() {

        const savedTheme = this.settings.theme;

        const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

        

        if (savedTheme === 'auto') {

            this.applyTheme(systemPrefersDark ? 'dark' : 'light');

        } else {

            this.applyTheme(savedTheme);

        }

    }


    applyTheme(theme = this.settings.theme) {

        const html = document.documentElement;

        

        if (theme === 'dark') {

            html.setAttribute('data-theme', 'dark');

            html.classList.add('dark-mode');

        } else {

            html.removeAttribute('data-theme');

            html.classList.remove('dark-mode');

        }

    }


    // ===== LANGUAGE MANAGEMENT =====

    setupLanguage() {

        this.applyLanguage(this.settings.language);

    }


    applyLanguage(lang = 'id') {

        document.documentElement.setAttribute('lang', lang);

        // In a real app, you would load translation files here

    }


    // ===== NOTIFICATION SYSTEM =====

    showNotification(message, type = 'info', duration = 5000) {

        const notification = document.createElement('div');

        notification.className = `notification notification-${type}`;

        notification.innerHTML = `

            <div class="notification-content">

                <i class="fas fa-${this.getNotificationIcon(type)}"></i>

                <span>${message}</span>

            </div>

            <button class="notification-close" onclick="this.parentElement.remove()">

                <i class="fas fa-times"></i>

            </button>

        `;


        // Add styles if not already added

        if (!document.querySelector('#notification-styles')) {

            const styles = document.createElement('style');

            styles.id = 'notification-styles';

            styles.textContent = `

                .notification {

                    position: fixed;

                    top: 20px;

                    right: 20px;

                    background: white;

                    border-radius: 8px;

                    padding: 15px 20px;

                    box-shadow: 0 4px 12px rgba(0,0,0,0.15);

                    border-left: 4px solid #2196F3;

                    z-index: 10000;

                    max-width: 400px;

                    animation: slideInRight 0.3s ease;

                }

                .notification-success { border-left-color: #4CAF50; }

                .notification-error { border-left-color: #f44336; }

                .notification-warning { border-left-color: #ff9800; }

                .notification-content {

                    display: flex;

                    align-items: center;

                    gap: 10px;

                }

                .notification-close {

                    background: none;

                    border: none;

                    cursor: pointer;

                    padding: 5px;

                    margin-left: 10px;

                }

                @keyframes slideInRight {

                    from { transform: translateX(100%); opacity: 0; }

                    to { transform: translateX(0); opacity: 1; }

                }

            `;

            document.head.appendChild(styles);

        }


        document.body.appendChild(notification);


        // Auto remove after duration

        if (duration > 0) {

            setTimeout(() => {

                if (notification.parentElement) {

                    notification.remove();

                }

            }, duration);

        }


        return notification;

    }


    getNotificationIcon(type) {

        const icons = {

            success: 'check-circle',

            error: 'exclamation-circle',

            warning: 'exclamation-triangle',

            info: 'info-circle'

        };

        return icons[type] || 'info-circle';

    }


    // ===== OFFLINE DETECTION =====

    setupOfflineDetection() {

        window.addEventListener('online', () => {

            this.showNotification('Koneksi internet kembali', 'success');

            this.syncOfflineData();

        });


        window.addEventListener('offline', () => {

            this.showNotification('Anda sedang offline', 'warning', 0);

        });

    }


    async syncOfflineData() {

        // Sync any pending data when coming back online

        const pendingActions = JSON.parse(localStorage.getItem('farmerSmartAI_pendingActions') || '[]');

        

        for (const action of pendingActions) {

            try {

                await this.mockAPICall(action.type, action.data);

            } catch (error) {

                console.error('Failed to sync action:', action, error);

            }

        }

        

        localStorage.removeItem('farmerSmartAI_pendingActions');

    }


    // ===== SERVICE WORKER =====

    setupServiceWorker() {

        if ('serviceWorker' in navigator) {

            window.addEventListener('load', async () => {

                try {

                    const registration = await navigator.serviceWorker.register('/sw.js');

                    console.log('ServiceWorker registered:', registration);

                } catch (error) {

                    console.log('ServiceWorker registration failed:', error);

                }

            });

        }

    }


    // ===== DATA FORMATTING =====

    formatNumber(number, decimals = 2) {

        return new Intl.NumberFormat('id-ID', {

            minimumFractionDigits: decimals,

            maximumFractionDigits: decimals

        }).format(number);

    }


    formatDate(date, options = {}) {

        const defaultOptions = {

            year: 'numeric',

            month: 'long',

            day: 'numeric'

        };

        

        return new Intl.DateTimeFormat('id-ID', { ...defaultOptions, ...options }).format(new Date(date));

    }


    formatCurrency(amount) {

        return new Intl.NumberFormat('id-ID', {

            style: 'currency',

            currency: 'IDR',

            minimumFractionDigits: 0

        }).format(amount);

    }


    formatPercentage(value, decimals = 1) {

        return `${this.formatNumber(value, decimals)}%`;

    }


    // ===== SENSOR DATA SIMULATION =====

    generateSensorData() {

        return {

            soil_moisture: this.randomInt(30, 80),

            temperature: this.randomInt(20, 35) + Math.random(),

            humidity: this.randomInt(40, 90),

            ph_level: this.randomInt(55, 75) / 10,

            nutrient_n: this.randomInt(20, 60),

            nutrient_p: this.randomInt(15, 40),

            nutrient_k: this.randomInt(10, 50),

            timestamp: new Date().toISOString()

        };

    }


    randomInt(min, max) {

        return Math.floor(Math.random() * (max - min + 1)) + min;

    }


    // ===== API SIMULATION =====

    async mockAPICall(endpoint, data) {

        // Simulate API delay

        await this.delay(1000 + Math.random() * 1000);


        const responses = {

            login: () => {

                if (data.email === 'demo@farmersmartai.com' && data.password === 'demo123') {

                    return {

                        success: true,

                        user: {

                            id: 1,

                            name: 'Petani Smart',

                            email: data.email,

                            farmName: 'Kebun Sejahtera',

                            farmSize: 5.2,

                            joinDate: '2023-01-15'

                        },

                        token: 'mock_jwt_token_' + Date.now()

                    };

                }

                return { success: false, message: 'Invalid credentials' };

            },


            register: () => {

                return { success: true, message: 'User registered successfully' };

            },


            sensorData: () => {

                return {

                    success: true,

                    data: Array.from({ length: 24 }, (_, i) => ({

                        ...this.generateSensorData(),

                        timestamp: new Date(Date.now() - i * 3600000).toISOString()

                    }))

                };

            },


            recommendations: () => {

                return {

                    success: true,

                    recommendations: [

                        {

                            type: 'irrigation',

                            priority: 'high',

                            message: 'Kelembaban tanah rendah, perlu irigasi',

                            action: 'schedule_irrigation',

                            parameters: { duration: 30, area: 'north' }

                        },

                        {

                            type: 'fertilization',

                            priority: 'medium',

                            message: 'Level kalium rendah, pertimbangkan pemupukan',

                            action: 'apply_fertilizer',

                            parameters: { type: 'KCL', amount: 50 }

                        }

                    ]

                };

            }

        };


        const handler = responses[endpoint];

        return handler ? handler() : { success: false, message: 'Endpoint not found' };

    }


    delay(ms) {

        return new Promise(resolve => setTimeout(resolve, ms));

    }


    // ===== FORM HANDLING =====

    setupFormValidation(formSelector) {

        const forms = document.querySelectorAll(formSelector);

        

        forms.forEach(form => {

            form.addEventListener('submit', (e) => {

                if (!this.validateForm(form)) {

                    e.preventDefault();

                    this.showNotification('Harap periksa form yang diisi', 'error');

                }

            });


            // Real-time validation

            const inputs = form.querySelectorAll('input[required], select[required], textarea[required]');

            inputs.forEach(input => {

                input.addEventListener('blur', () => this.validateField(input));

                input.addEventListener('input', () => this.clearFieldError(input));

            });

        });

    }


    validateForm(form) {

        let isValid = true;

        const inputs = form.querySelectorAll('input[required], select[required], textarea[required]');

        

        inputs.forEach(input => {

            if (!this.validateField(input)) {

                isValid = false;

            }

        });


        return isValid;

    }


    validateField(field) {

        this.clearFieldError(field);


        let isValid = true;

        let errorMessage = '';


        // Check required fields

        if (field.hasAttribute('required') && !field.value.trim()) {

            isValid = false;

            errorMessage = field.getAttribute('data-error-required') || 'Field ini wajib diisi';

        }


        // Email validation

        if (field.type === 'email' && field.value) {

            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

            if (!emailRegex.test(field.value)) {

                isValid = false;

                errorMessage = field.getAttribute('data-error-email') || 'Format email tidak valid';

            }

        }


        // Password strength

        if (field.type === 'password' && field.value) {

            if (field.value.length < 8) {

                isValid = false;

                errorMessage = 'Password harus minimal 8 karakter';

            }

        }


        // Phone number validation

        if (field.type === 'tel' && field.value) {

            const phoneRegex = /^[+]?[\d\s\-()]{10,}$/;

            if (!phoneRegex.test(field.value)) {

                isValid = false;

                errorMessage = 'Format nomor telepon tidak valid';

            }

        }


        if (!isValid) {

            this.showFieldError(field, errorMessage);

        }


        return isValid;

    }


    showFieldError(field, message) {

        field.classList.add('error');

        

        let errorElement = field.parentNode.querySelector('.field-error');

        if (!errorElement) {

            errorElement = document.createElement('div');

            errorElement.className = 'field-error';

            field.parentNode.appendChild(errorElement);

        }

        

        errorElement.textContent = message;

    }


    clearFieldError(field) {

        field.classList.remove('error');

        const errorElement = field.parentNode.querySelector('.field-error');

        if (errorElement) {

            errorElement.remove();

        }

    }


    // ===== CHART HELPERS =====

    createChart(canvasId, config) {

        const ctx = document.getElementById(canvasId)?.getContext('2d');

        if (!ctx) {

            console.error(`Canvas with id ${canvasId} not found`);

            return null;

        }


        const defaultConfig = {

            type: 'line',

            options: {

                responsive: true,

                maintainAspectRatio: false,

                plugins: {

                    legend: {

                        position: 'top',

                    }

                }

            }

        };


        return new Chart(ctx, { ...defaultConfig, ...config });

    }


    // ===== EVENT LISTENERS SETUP =====

    setupEventListeners() {

        // Mobile menu toggle

        this.setupMobileMenu();


        // Logout buttons

        this.setupLogoutHandlers();


        // Theme toggle

        this.setupThemeToggle();


        // Form submissions

        this.setupFormValidation('form[data-validate]');


        // Tab navigation

        this.setupTabNavigation();


        // Modal handling

        this.setupModalHandlers();

    }


    setupMobileMenu() {

        const menuButtons = document.querySelectorAll('.mobile-menu-btn');

        const sidebar = document.querySelector('.sidebar');

        const overlay = document.createElement('div');

        overlay.className = 'sidebar-overlay';


        menuButtons.forEach(btn => {

            btn.addEventListener('click', () => {

                sidebar.classList.toggle('active');

                if (sidebar.classList.contains('active')) {

                    document.body.appendChild(overlay);

                    document.body.style.overflow = 'hidden';

                } else {

                    overlay.remove();

                    document.body.style.overflow = '';

                }

            });

        });


        // Close sidebar when clicking overlay

        overlay.addEventListener('click', () => {

            sidebar.classList.remove('active');

            overlay.remove();

            document.body.style.overflow = '';

        });

    }


    setupLogoutHandlers() {

        const logoutButtons = document.querySelectorAll('[data-action="logout"]');

        logoutButtons.forEach(btn => {

            btn.addEventListener('click', (e) => {

                e.preventDefault();

                if (confirm('Apakah Anda yakin ingin logout?')) {

                    this.logout();

                }

            });

        });

    }


    setupThemeToggle() {

        const themeToggles = document.querySelectorAll('[data-action="toggle-theme"]');

        themeToggles.forEach(toggle => {

            toggle.addEventListener('click', () => {

                const newTheme = this.settings.theme === 'dark' ? 'light' : 'dark';

                this.saveSettings({ theme: newTheme });

                this.showNotification(`Tema diubah ke ${newTheme === 'dark' ? 'gelap' : 'terang'}`, 'success');

            });

        });

    }


    setupTabNavigation() {

        const tabContainers = document.querySelectorAll('.tab-container, .settings-tabs');

        

        tabContainers.forEach(container => {

            const tabs = container.querySelectorAll('.tab');

            const contents = container.querySelectorAll('.tab-content');

            

            tabs.forEach(tab => {

                tab.addEventListener('click', () => {

                    const target = tab.getAttribute('data-tab');

                    

                    // Update active tab

                    tabs.forEach(t => t.classList.remove('active'));

                    tab.classList.add('active');

                    

                    // Show target content

                    contents.forEach(content => {

                        content.classList.remove('active');

                        if (content.id === `${target}-tab`) {

                            content.classList.add('active');

                        }

                    });

                });

            });

        });

    }


    setupModalHandlers() {

        const modalTriggers = document.querySelectorAll('[data-modal]');

        const modals = document.querySelectorAll('.modal');

        

        modalTriggers.forEach(trigger => {

            trigger.addEventListener('click', () => {

                const modalId = trigger.getAttribute('data-modal');

                const modal = document.getElementById(modalId);

                if (modal) {

                    modal.classList.add('active');

                    document.body.style.overflow = 'hidden';

                }

            });

        });


        // Close modals

        modals.forEach(modal => {

            const closeBtn = modal.querySelector('.modal-close');

            if (closeBtn) {

                closeBtn.addEventListener('click', () => {

                    modal.classList.remove('active');

                    document.body.style.overflow = '';

                });

            }


            // Close on overlay click

            modal.addEventListener('click', (e) => {

                if (e.target === modal) {

                    modal.classList.remove('active');

                    document.body.style.overflow = '';

                }

            });

        });

    }


    // ===== PERFORMANCE OPTIMIZATION =====

    debounce(func, wait) {

        let timeout;

        return function executedFunction(...args) {

            const later = () => {

                clearTimeout(timeout);

                func(...args);

            };

            clearTimeout(timeout);

            timeout = setTimeout(later, wait);

        };

    }


    throttle(func, limit) {

        let inThrottle;

        return function(...args) {

            if (!inThrottle) {

                func.apply(this, args);

                inThrottle = true;

                setTimeout(() => inThrottle = false, limit);

            }

        };

    }


    // ===== EXPORT DATA =====

    exportToCSV(data, filename) {

        if (!data || !data.length) return;


        const headers = Object.keys(data[0]);

        const csvContent = [

            headers.join(','),

            ...data.map(row => headers.map(header => JSON.stringify(row[header])).join(','))

        ].join('\n');


        this.downloadFile(csvContent, filename, 'text/csv');

    }


    exportToJSON(data, filename) {

        const jsonContent = JSON.stringify(data, null, 2);

        this.downloadFile(jsonContent, filename, 'application/json');

    }


    downloadFile(content, filename, contentType) {

        const blob = new Blob([content], { type: contentType });

        const url = URL.createObjectURL(blob);

        const link = document.createElement('a');

        link.href = url;

        link.download = filename;

        document.body.appendChild(link);

        link.click();

        document.body.removeChild(link);

        URL.revokeObjectURL(url);

    }


    // ===== SENSOR DATA MANAGEMENT =====

    async getSensorData(range = '24h') {

        try {

            const response = await this.mockAPICall('sensorData', { range });

            return response.success ? response.data : [];

        } catch (error) {

            console.error('Error fetching sensor data:', error);

            return [];

        }

    }


    async getAIRecommendations() {

        try {

            const response = await this.mockAPICall('recommendations', {});

            return response.success ? response.recommendations : [];

        } catch (error) {

            console.error('Error fetching recommendations:', error);

            return [];

        }

    }

}


// ===== GLOBAL INITIALIZATION =====

let app;


document.addEventListener('DOMContentLoaded', function() {

    app = new FarmerSmartAI();

    

    // Make app globally available for HTML event handlers

    window.FarmerSmartAI = app;

    

    console.log('FarmerSmartAI initialized');

});


// ===== UTILITY FUNCTIONS (Global) =====

window.formatNumber = (number, decimals = 2) => {

    return app ? app.formatNumber(number, decimals) : number.toFixed(decimals);

};


window.formatDate = (date, options) => {

    return app ? app.formatDate(date, options) : new Date(date).toLocaleDateString('id-ID');

};


window.showNotification = (message, type, duration) => {

    return app ? app.showNotification(message, type, duration) : console.log(`[${type}] ${message}`);

};


// ===== ERROR HANDLING =====

window.addEventListener('error', (event) => {

    console.error('Global error:', event.error);

    if (app) {

        app.showNotification('Terjadi kesalahan sistem', 'error');

    }

});


window.addEventListener('unhandledrejection', (event) => {

    console.error('Unhandled promise rejection:', event.reason);

    event.preventDefault();

});

```


Cara Penggunaan:


1. Simpan file sebagai main.js di folder JS project Anda

2. Hubungkan ke HTML dengan menambahkan sebelum penutup </body>:


```html

<script src="js/main.js"></script>

```


Fitur Utama Main.js:


πŸ—️ Architecture & Initialization


· Class-based structure untuk organized code

· Auto-initialization pada DOM ready

· Comprehensive error handling


πŸ‘€ User Management


· Login/logout functionality

· User session persistence

· Profile data management


⚙️ Settings System


· Theme management (light/dark/auto)

· Language support

· Persistent settings storage


πŸ”” Notification System


· Toast notifications dengan berbagai types

· Auto-dismiss functionality

· Customizable duration


πŸ“± Responsive Behavior


· Mobile menu handling

· Touch event optimization

· Responsive utilities


πŸ“Š Data Management


· Sensor data simulation

· API call abstractions

· Offline data synchronization


🎨 UI Utilities


· Form validation system

· Modal management

· Tab navigation

· Chart helpers


πŸ“ Export Features


· CSV export untuk data

· JSON export

· File download utilities


⚡ Performance


· Debounce dan throttle functions

· Efficient event handling

· Memory management


πŸ”§ Development Tools


· Mock API untuk development

· Debug logging

· Error tracking


File ini menyediakan foundation 𝗒ang solid untuk seluruh aplikasi FarmerSmartAI dengan fungsi-fungsi yang reusable dan maintainable.



π–‘π–Ύπ—‹π—Œπ–Ίπ—†π–»π—Žπ—‡π—€ 𝗄𝖾: π–ͺπ—ˆπ–½π–Ύ π–¨π—Œπ—‚ π–₯𝗂𝗅𝖾 dashboard.js










Comments

Popular posts from this blog

Aplikasi FarmerSmartAI:

icon-generator.html