/** * Supera Glia - Funções Utilitárias * @module core/utils */ (function() { 'use strict'; SuperaGlia.utils = { /** * Formata valor monetário para BRL * @param {number} value - Valor a formatar * @returns {string} Valor formatado */ formatMoney: (value) => { if (!value && value !== 0) return 'R$ 0,00'; return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value); }, /** * Formata data para padrão brasileiro * @param {string|Date} date - Data a formatar * @returns {string} Data formatada */ formatDate: (date) => { if (!date) return '-'; try { if (typeof date === 'string' && date.includes('T')) { const dateStr = date.split('T')[0]; const [year, month, day] = dateStr.split('-'); return `${day}/${month}/${year}`; } return new Date(date).toLocaleDateString('pt-BR'); } catch { return date; } }, /** * Formata data e hora * @param {string|Date} dateStr - Data/hora a formatar * @returns {string} Data/hora formatada */ formatDateTime: (dateStr) => { if (!dateStr) return '-'; const date = new Date(dateStr); return `${date.toLocaleDateString('pt-BR')} ${date.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })}`; }, /** * Formata número de telefone brasileiro * @param {string} phone - Telefone a formatar * @returns {string} Telefone formatado */ formatPhone: (phone) => { if (!phone) return '-'; const cleaned = phone.replace(/\D/g, ''); if (cleaned.length === 11) { return `(${cleaned.slice(0, 2)}) ${cleaned.slice(2, 7)}-${cleaned.slice(7)}`; } else if (cleaned.length === 10) { return `(${cleaned.slice(0, 2)}) ${cleaned.slice(2, 6)}-${cleaned.slice(6)}`; } return phone; }, /** * Normaliza telefone para formato padrão (apenas números) * @param {string} phone - Telefone * @returns {string} Telefone normalizado */ normalizePhone: (phone) => { if (!phone) return ''; let cleaned = phone.replace(/\D/g, ''); // Remove 55 do início se presente if (cleaned.startsWith('55') && cleaned.length > 11) { cleaned = cleaned.slice(2); } // Adiciona 9 se necessário (8 dígitos no número) if (cleaned.length === 10) { cleaned = cleaned.slice(0, 2) + '9' + cleaned.slice(2); } return cleaned; }, /** * Cria URL de WhatsApp * @param {string} phone - Telefone * @param {string} message - Mensagem opcional * @returns {string} URL do WhatsApp */ createWhatsAppUrl: (phone, message = '') => { const normalized = SuperaGlia.utils.normalizePhone(phone); const baseUrl = `https://wa.me/55${normalized}`; return message ? `${baseUrl}?text=${encodeURIComponent(message)}` : baseUrl; }, /** * Debounce function * @param {Function} func - Função a debounce * @param {number} wait - Tempo de espera em ms * @returns {Function} Função com debounce */ debounce: (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, /** * Capitaliza primeira letra * @param {string} str - String * @returns {string} String capitalizada */ capitalize: (str) => { if (!str) return ''; return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); }, /** * Calcula idade a partir da data de nascimento * @param {string|Date} birthDate - Data de nascimento * @returns {number} Idade */ calculateAge: (birthDate) => { if (!birthDate) return 0; const birth = new Date(birthDate); const today = new Date(); let age = today.getFullYear() - birth.getFullYear(); const monthDiff = today.getMonth() - birth.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) { age--; } return age; }, /** * Gera cor baseada no hash de uma string * @param {string} str - String base * @returns {string} Código de cor hex */ stringToColor: (str) => { if (!str) return '#808080'; let hash = 0; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } let color = '#'; for (let i = 0; i < 3; i++) { const value = (hash >> (i * 8)) & 0xFF; color += ('00' + value.toString(16)).substr(-2); } return color; }, /** * Faz requisição à API com tratamento de erro * @param {string} endpoint - Endpoint da API * @param {object} options - Opções do fetch * @returns {Promise} Dados da resposta */ apiRequest: async (endpoint, options = {}) => { const { API_BASE } = SuperaGlia.config; const url = endpoint.startsWith('http') ? endpoint : `${API_BASE}${endpoint}`; try { const response = await fetch(url, { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return await response.json(); } catch (error) { console.error(`Erro na API [${endpoint}]:`, error); throw error; } }, /** * Gera data default para filtros (X dias atrás) * @param {number} daysAgo - Dias no passado * @returns {string} Data em formato ISO */ getDateDaysAgo: (daysAgo) => { const date = new Date(); date.setDate(date.getDate() - daysAgo); return date.toISOString().split('T')[0]; }, /** * Obtém data de hoje em formato ISO * @returns {string} Data de hoje */ getToday: () => { return new Date().toISOString().split('T')[0]; }, /** * Obtém primeiro dia do mês atual * @returns {string} Primeiro dia do mês */ getFirstDayOfMonth: () => { const date = new Date(); date.setDate(1); return date.toISOString().split('T')[0]; }, /** * Verifica se uma data é hoje * @param {string|Date} date - Data a verificar * @returns {boolean} */ isToday: (date) => { if (!date) return false; const today = new Date().toDateString(); const checkDate = new Date(date).toDateString(); return today === checkDate; } }; })();