/** * Supera Glia - Modal de Detalhes do Aluno * @module components/AlunoDetalhesModal */ (function() { 'use strict'; SuperaGlia.initAlunoDetalhesModal = function() { const { useState } = SuperaGlia.hooks; const { API_BASE } = SuperaGlia.config; const { Icon, perfilColors } = SuperaGlia; /** * Modal de detalhes e edição do aluno * @param {object} props - { aluno, onClose, onUpdate } */ SuperaGlia.AlunoDetalhesModal = ({ aluno, onClose, onUpdate }) => { if (!aluno) return null; const [editMode, setEditMode] = useState(false); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(''); const [saveSuccess, setSaveSuccess] = useState(false); const [loadingChatwoot, setLoadingChatwoot] = useState(false); // Estados editáveis (atributos do Chatwoot) const [editData, setEditData] = useState({ ausente_ate: aluno.ausente_ate ? aluno.ausente_ate.split('T')[0] : '', motivo_ausencia: aluno.motivo_ausencia || '', status_aluno: aluno.status_aluno || '', turma: aluno.turma || '', sala: aluno.sala || aluno.sala_aluno || '', cod_aluno: aluno.cod_aluno || aluno.id || '', idade: aluno.idade || '', dias_no_supera: aluno.dias_no_supera || '', professor: aluno.professor || '', responsavel_financeiro: aluno.resp_financeiro_nome || aluno.responsavel || '', tel_resp_financeiro: aluno.resp_financeiro_celular || '', situacao_matricula: aluno.situacao_matricula || '', parcela_atual: aluno.parcela_atual || aluno.descricao_titulo || '', observacoes: aluno.observacoes || '' }); const statusOptions = ['', 'Ativo', 'Em Férias', 'Afastado Temporário', 'Inativo']; const situacaoOptions = ['', 'Vigente', 'Cancelada', 'Suspensa', 'Trancada']; const carregarDadosChatwoot = async () => { setLoadingChatwoot(true); setSaveError(''); try { const telefone = aluno.telefone || aluno.resp_financeiro_celular || ''; const codAluno = aluno.cod_aluno || aluno.id || ''; const params = new URLSearchParams(); if (telefone) params.append('telefone', telefone); if (codAluno) params.append('cod_aluno', codAluno); const response = await fetch(`${API_BASE}/chatwoot/contato?${params}`); const result = await response.json(); if (result.success && result.atributos) { const attrs = result.atributos; setEditData(prev => ({ ...prev, ausente_ate: attrs.ausente_ate ? attrs.ausente_ate.split('T')[0] : prev.ausente_ate, motivo_ausencia: attrs.motivo_ausencia || prev.motivo_ausencia, status_aluno: attrs.status_aluno || prev.status_aluno, turma: attrs.turma || prev.turma, sala: attrs.sala || prev.sala, cod_aluno: attrs.cod_aluno || prev.cod_aluno, idade: attrs.idade || prev.idade, dias_no_supera: attrs.dias_no_supera || prev.dias_no_supera, professor: attrs.professor || prev.professor, responsavel_financeiro: attrs.responsavel_financeiro || prev.responsavel_financeiro, tel_resp_financeiro: attrs.tel_resp_financeiro || prev.tel_resp_financeiro, situacao_matricula: attrs.situacao_matricula || prev.situacao_matricula, parcela_atual: attrs.parcela_atual || prev.parcela_atual, observacoes: attrs.observacoes || prev.observacoes })); } } catch (e) { console.error('Erro ao carregar dados do Chatwoot:', e); } finally { setLoadingChatwoot(false); } }; const handleEditClick = () => { if (!editMode) { carregarDadosChatwoot(); } else { // Resetar dados ao cancelar setEditData({ ausente_ate: aluno.ausente_ate ? aluno.ausente_ate.split('T')[0] : '', motivo_ausencia: aluno.motivo_ausencia || '', status_aluno: aluno.status_aluno || '', turma: aluno.turma || '', sala: aluno.sala || aluno.sala_aluno || '', cod_aluno: aluno.cod_aluno || aluno.id || '', idade: aluno.idade || '', dias_no_supera: aluno.dias_no_supera || '', professor: aluno.professor || '', responsavel_financeiro: aluno.resp_financeiro_nome || aluno.responsavel || '', tel_resp_financeiro: aluno.resp_financeiro_celular || '', situacao_matricula: aluno.situacao_matricula || '', parcela_atual: aluno.parcela_atual || aluno.descricao_titulo || '', observacoes: aluno.observacoes || '' }); setSaveError(''); } setEditMode(!editMode); }; const formatDate = (dateStr) => { if (!dateStr) return '-'; const date = new Date(dateStr); return date.toLocaleDateString('pt-BR'); }; const handleInputChange = (field, value) => { setEditData(prev => ({ ...prev, [field]: value })); }; const handleSave = async () => { setSaving(true); setSaveError(''); setSaveSuccess(false); try { const telefone = aluno.telefone || aluno.resp_financeiro_celular || ''; if (!telefone) { throw new Error('Telefone não encontrado para atualizar contato'); } const response = await fetch(`${API_BASE}/chatwoot/atualizar-contato`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ telefone, atributos: { ausente_ate: editData.ausente_ate || null, motivo_ausencia: editData.motivo_ausencia || null, status_aluno: editData.status_aluno || null, turma: editData.turma || null, sala: editData.sala || null, cod_aluno: editData.cod_aluno || null, idade: editData.idade ? parseInt(editData.idade) : null, dias_no_supera: editData.dias_no_supera ? parseInt(editData.dias_no_supera) : null, professor: editData.professor || null, responsavel_financeiro: editData.responsavel_financeiro || null, tel_resp_financeiro: editData.tel_resp_financeiro || null, situacao_matricula: editData.situacao_matricula || null, parcela_atual: editData.parcela_atual || null, observacoes: editData.observacoes || null } }) }); const result = await response.json(); if (result.success) { setSaveSuccess(true); setTimeout(() => { setEditMode(false); setSaveSuccess(false); if (onUpdate) onUpdate(); }, 1500); } else { throw new Error(result.error || 'Erro ao salvar'); } } catch (err) { setSaveError(err.message || 'Erro ao atualizar atributos'); } finally { setSaving(false); } }; const isAusente = aluno.ausente_ate && new Date(aluno.ausente_ate) >= new Date(); const colors = perfilColors[aluno.perfil] || perfilColors['60+']; const campos = [ { label: 'Código Aluno', value: aluno.cod_aluno || aluno.id, icon: 'hash' }, { label: 'Turma', value: aluno.turma, icon: 'users' }, { label: 'Sala', value: aluno.sala || aluno.sala_aluno, icon: 'door-open' }, { label: 'Professor', value: aluno.professor, icon: 'graduation-cap' }, { label: 'Idade', value: aluno.idade ? `${aluno.idade} anos` : '-', icon: 'cake' }, { label: 'Dias no Supera', value: aluno.dias_no_supera || '-', icon: 'calendar-check' }, { label: 'Situação Matrícula', value: aluno.situacao_matricula || '-', icon: 'file-check' }, { label: 'Parcela Atual', value: aluno.parcela_atual || aluno.descricao_titulo || '-', icon: 'receipt' }, { label: 'Resp. Financeiro', value: aluno.resp_financeiro_nome || aluno.responsavel || '-', icon: 'user-check' }, { label: 'Tel. Resp. Financeiro', value: aluno.resp_financeiro_celular || '-', icon: 'phone' }, { label: 'Telefone Aluno', value: aluno.telefone || '-', icon: 'smartphone' }, { label: 'E-mail', value: aluno.email || '-', icon: 'mail' }, isAusente && { label: 'Ausente Até', value: formatDate(aluno.ausente_ate), icon: 'calendar-x' }, isAusente && { label: 'Motivo Ausência', value: aluno.motivo_ausencia || '-', icon: 'plane' } ].filter(Boolean); // Campos editáveis para o modo de edição const camposEditaveis = [ { key: 'ausente_ate', label: 'Ausente Até', type: 'date' }, { key: 'motivo_ausencia', label: 'Motivo Ausência', type: 'text', placeholder: 'Ex: Viagem, Saúde...' }, { key: 'status_aluno', label: 'Status Aluno', type: 'select', options: statusOptions }, { key: 'turma', label: 'Turma', type: 'text' }, { key: 'sala', label: 'Sala', type: 'text' }, { key: 'professor', label: 'Professor', type: 'text' }, { key: 'cod_aluno', label: 'Código Aluno', type: 'text' }, { key: 'idade', label: 'Idade', type: 'number' }, { key: 'dias_no_supera', label: 'Dias no Supera', type: 'number' }, { key: 'responsavel_financeiro', label: 'Responsável Financeiro', type: 'text' }, { key: 'tel_resp_financeiro', label: 'Tel. Resp. Financeiro', type: 'text' }, { key: 'situacao_matricula', label: 'Situação Matrícula', type: 'select', options: situacaoOptions }, { key: 'parcela_atual', label: 'Parcela Atual', type: 'text', placeholder: 'Ex: Mód. Supera - 12ª/18' } ]; const renderEditField = (campo) => { if (campo.type === 'select') { return React.createElement('select', { value: editData[campo.key], onChange: e => handleInputChange(campo.key, e.target.value), className: 'w-full px-3 py-2.5 bg-gray-50 border-2 border-gray-200 rounded-xl text-sm transition-all cursor-pointer' }, campo.options.map(opt => React.createElement('option', { key: opt, value: opt }, opt || 'Selecione...'))); } return React.createElement('input', { type: campo.type, value: editData[campo.key], onChange: e => handleInputChange(campo.key, e.target.value), placeholder: campo.placeholder || '', className: 'w-full px-3 py-2.5 bg-gray-50 border-2 border-gray-200 rounded-xl text-sm transition-all' }); }; return React.createElement('div', { className: 'fixed inset-0 modal-overlay z-50 flex items-center justify-center p-4', onClick: onClose }, React.createElement('div', { className: 'bg-white rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-hidden', onClick: e => e.stopPropagation() }, // Header React.createElement('div', { className: `p-6 ${colors.bg}` }, React.createElement('div', { className: 'flex items-start justify-between' }, React.createElement('div', { className: 'flex items-center gap-4' }, React.createElement('div', { className: `w-16 h-16 rounded-2xl ${colors.text} bg-white/50 flex items-center justify-center` }, React.createElement('span', { className: 'text-2xl font-bold' }, aluno.nome?.[0] || '?') ), React.createElement('div', null, React.createElement('h2', { className: `text-xl font-bold ${colors.text}` }, aluno.nome), React.createElement('div', { className: 'flex items-center gap-2 mt-1' }, React.createElement('span', { className: `text-sm px-2 py-0.5 rounded-full bg-white/50 ${colors.text}` }, aluno.perfil), isAusente && React.createElement('span', { className: 'ausente-badge text-sm px-2 py-0.5 rounded-full bg-orange-500 text-white flex items-center gap-1' }, React.createElement(Icon, { name: 'plane', className: 'w-3 h-3' }), 'Ausente' ) ) ) ), React.createElement('button', { onClick: onClose, className: 'p-2 hover:bg-white/30 rounded-lg transition-colors' }, React.createElement(Icon, { name: 'x', className: `w-5 h-5 ${colors.text}` }) ) ) ), // Ausência banner (se aplicável e não em modo de edição) !editMode && isAusente && React.createElement('div', { className: 'px-6 py-4 bg-orange-50 border-b border-orange-100' }, React.createElement('div', { className: 'flex items-center gap-3' }, React.createElement(Icon, { name: 'calendar-x', className: 'w-5 h-5 text-orange-600' }), React.createElement('div', null, React.createElement('p', { className: 'font-medium text-orange-800' }, `Ausente até ${formatDate(aluno.ausente_ate)}`), aluno.motivo_ausencia && React.createElement('p', { className: 'text-sm text-orange-600' }, `Motivo: ${aluno.motivo_ausencia}`) ) ) ), // Conteúdo principal React.createElement('div', { className: 'p-6 overflow-y-auto max-h-[50vh]' }, editMode // MODO EDIÇÃO ? React.createElement('div', { className: 'space-y-4' }, React.createElement('div', { className: 'flex items-center gap-2 mb-4 pb-3 border-b border-gray-200' }, React.createElement(Icon, { name: 'edit-3', className: 'w-5 h-5 text-orange-500' }), React.createElement('h3', { className: 'font-semibold text-gray-900' }, 'Editar Atributos do Contato'), React.createElement('span', { className: 'text-xs px-2 py-1 bg-blue-100 text-blue-700 rounded-full' }, 'Chatwoot'), loadingChatwoot && React.createElement('div', { className: 'ml-2 w-4 h-4 border-2 border-orange-200 border-t-orange-500 rounded-full animate-spin' }) ), loadingChatwoot ? React.createElement('div', { className: 'flex items-center justify-center py-8' }, React.createElement('div', { className: 'text-center' }, React.createElement('div', { className: 'w-8 h-8 border-4 border-orange-200 border-t-orange-500 rounded-full animate-spin mx-auto mb-3' }), React.createElement('p', { className: 'text-sm text-gray-500' }, 'Carregando dados do Chatwoot...') ) ) : React.createElement(React.Fragment, null, React.createElement('div', { className: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, camposEditaveis.map(campo => React.createElement('div', { key: campo.key }, React.createElement('label', { className: 'block text-xs font-semibold text-gray-500 mb-1.5 uppercase' }, campo.label), renderEditField(campo) ) ) ), React.createElement('div', { className: 'mt-4' }, React.createElement('label', { className: 'block text-xs font-semibold text-gray-500 mb-1.5 uppercase' }, 'Observações'), React.createElement('textarea', { value: editData.observacoes, onChange: e => handleInputChange('observacoes', e.target.value), placeholder: 'Observações sobre o aluno...', rows: 3, className: 'w-full px-3 py-2.5 bg-gray-50 border-2 border-gray-200 rounded-xl text-sm transition-all resize-none' }) ), saveError && React.createElement('div', { className: 'p-3 bg-red-50 border border-red-200 rounded-xl' }, React.createElement('p', { className: 'text-red-600 text-sm' }, saveError) ), saveSuccess && React.createElement('div', { className: 'p-3 bg-green-50 border border-green-200 rounded-xl flex items-center gap-2' }, React.createElement(Icon, { name: 'check-circle', className: 'w-5 h-5 text-green-600' }), React.createElement('p', { className: 'text-green-600 text-sm font-medium' }, 'Atributos atualizados com sucesso!') ) ) ) // MODO VISUALIZAÇÃO : React.createElement('div', null, React.createElement('div', { className: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, campos.map((campo, i) => React.createElement('div', { key: i, className: 'flex items-start gap-3 p-3 bg-gray-50 rounded-lg' }, React.createElement(Icon, { name: campo.icon, className: 'w-4 h-4 text-gray-400 mt-0.5' }), React.createElement('div', null, React.createElement('p', { className: 'text-xs text-gray-500 uppercase tracking-wide' }, campo.label), React.createElement('p', { className: 'font-medium text-gray-900' }, campo.value || '-') ) ) ) ), aluno.observacoes && React.createElement('div', { className: 'mt-4 p-4 bg-yellow-50 rounded-lg border border-yellow-100' }, React.createElement('div', { className: 'flex items-start gap-2' }, React.createElement(Icon, { name: 'message-square', className: 'w-4 h-4 text-yellow-600 mt-0.5' }), React.createElement('div', null, React.createElement('p', { className: 'text-xs text-yellow-700 uppercase tracking-wide mb-1' }, 'Observações'), React.createElement('p', { className: 'text-sm text-yellow-800' }, aluno.observacoes) ) ) ) ) ), // Footer React.createElement('div', { className: 'px-6 py-4 bg-gray-50 border-t flex justify-between items-center' }, React.createElement('button', { onClick: handleEditClick, disabled: loadingChatwoot, className: `flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-colors disabled:opacity-50 ${editMode ? 'bg-gray-200 hover:bg-gray-300 text-gray-700' : 'bg-blue-500 hover:bg-blue-600 text-white'}` }, loadingChatwoot ? React.createElement('div', { className: 'w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin' }) : React.createElement(Icon, { name: editMode ? 'x' : 'edit-3', className: 'w-4 h-4' }), loadingChatwoot ? 'Carregando...' : (editMode ? 'Cancelar' : 'Editar Atributos') ), React.createElement('div', { className: 'flex gap-2' }, editMode ? React.createElement('button', { onClick: handleSave, disabled: saving || loadingChatwoot, className: 'flex items-center gap-2 px-4 py-2 bg-orange-500 hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg text-white font-medium transition-colors' }, saving ? React.createElement('div', { className: 'w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin' }) : React.createElement(Icon, { name: 'save', className: 'w-4 h-4' }), saving ? 'Salvando...' : 'Salvar' ) : React.createElement('button', { onClick: onClose, className: 'px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-lg text-gray-700 font-medium transition-colors' }, 'Fechar') ) ) ) ); }; }; })();