Modernizacao completa do Estudio de Cartazes Pedagogicos: correcao de abertura, BNCC, upload de fotos, novo motor de IA, edicao direta e suite de exportacao
This commit is contained in:
+550
-203
File diff suppressed because it is too large
Load Diff
+229
-97
@@ -2420,109 +2420,181 @@
|
|||||||
<!-- MODAL: CRIAR CARTAZES -->
|
<!-- MODAL: CRIAR CARTAZES -->
|
||||||
<!-- ========================================== -->
|
<!-- ========================================== -->
|
||||||
<div id="cartazesModal" class="settings-modal" style="display: none;">
|
<div id="cartazesModal" class="settings-modal" style="display: none;">
|
||||||
<div class="settings-modal-content" style="max-width: 1100px; width: 95%; max-height: 95vh; display: flex; flex-direction: column;">
|
<div class="settings-modal-content" style="max-width: 1180px; width: 96%; max-height: 94vh; display: flex; flex-direction: column; border-radius: 16px; overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5);">
|
||||||
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light);">
|
|
||||||
<h3 style="display: flex; align-items: center; gap: 8px; font-family: 'Outfit', sans-serif;">🖼️ Estúdio de Cartazes PedaGog</h3>
|
<!-- Cabeçalho do Estúdio de Cartazes -->
|
||||||
<div style="display: flex; gap: 10px;">
|
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light); padding: 14px 20px; display: flex; justify-content: space-between; align-items: center;">
|
||||||
<button id="btnCloseCartazesModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem;">×</button>
|
<div style="display: flex; align-items: center; gap: 10px;">
|
||||||
|
<div style="background: rgba(16, 185, 129, 0.15); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.3); border-radius: 8px; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem;">
|
||||||
|
🖼️
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 style="margin: 0; font-family: 'Outfit', sans-serif; font-size: 1.15rem; color: var(--text-primary); display: flex; align-items: center; gap: 8px;">
|
||||||
|
Estúdio de Cartazes Pedagógicos
|
||||||
|
<span style="font-size: 0.68rem; font-weight: 700; background: linear-gradient(135deg, rgba(16,185,129,0.2), rgba(59,130,246,0.2)); color: #34d399; border: 1px solid rgba(16,185,129,0.4); padding: 2px 8px; border-radius: 12px;">BNCC & Design IA</span>
|
||||||
|
</h3>
|
||||||
|
<p style="margin: 2px 0 0 0; font-size: 0.76rem; color: var(--text-secondary);">Crie, personalize e imprima cartazes educativos, combinados de rotina e avisos em alta resolução</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<button id="btnCloseCartazesModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.6rem; padding: 4px; line-height: 1; transition: color 0.2s;" title="Fechar estúdio">×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Abas do Modal -->
|
<!-- Abas do Modal -->
|
||||||
<div style="padding: 12px 20px; display: flex; gap: 15px; border-bottom: 1px solid var(--border-light); background: var(--bg-primary);">
|
<div style="padding: 10px 20px; display: flex; gap: 10px; border-bottom: 1px solid var(--border-light); background: var(--bg-primary);">
|
||||||
<button id="tabCartazesCriar" class="tab-btn active" style="background: transparent; border: none; color: var(--text-primary); font-weight: 600; padding: 6px 12px; border-radius: 6px; cursor: pointer;">✨ Criar Cartaz</button>
|
<button id="tabCartazesCriar" class="tab-btn active" style="background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.3); color: #10b981; font-weight: 700; padding: 7px 16px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; gap: 6px; font-size: 0.84rem;">
|
||||||
<button id="tabCartazesHistorico" class="tab-btn" style="background: transparent; border: none; color: var(--text-secondary); font-weight: 600; padding: 6px 12px; border-radius: 6px; cursor: pointer;">🕒 Seus Cartazes Salvos</button>
|
<span>✨ Criador & Diagramação</span>
|
||||||
|
</button>
|
||||||
|
<button id="tabCartazesHistorico" class="tab-btn" style="background: transparent; border: 1px solid transparent; color: var(--text-secondary); font-weight: 600; padding: 7px 16px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; gap: 6px; font-size: 0.84rem;">
|
||||||
|
<span>🕒 Seus Cartazes Salvos</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; flex: 1; display: flex; flex-direction: column;">
|
<div class="settings-modal-body" style="padding: 18px 20px; overflow-y: auto; flex: 1; display: flex; flex-direction: column; background: var(--bg-secondary);">
|
||||||
|
|
||||||
<!-- Conteúdo 1: Criador -->
|
<!-- Conteúdo 1: Criador -->
|
||||||
<div id="panelCartazesCriar" style="display: flex; flex-direction: row; gap: 20px; height: 100%; flex-wrap: wrap;">
|
<div id="panelCartazesCriar" style="display: flex; flex-direction: row; gap: 22px; height: 100%; flex-wrap: wrap;">
|
||||||
|
|
||||||
<!-- Lado Esquerdo: Configurações e Templates -->
|
<!-- Lado Esquerdo: Configurações e Inteligência Pedagógica -->
|
||||||
<div id="cartazLeftConfigPanel" style="flex: 1; min-width: 300px; display: flex; flex-direction: column; gap: 14px;">
|
<div id="cartazLeftConfigPanel" style="flex: 1; min-width: 320px; display: flex; flex-direction: column; gap: 14px;">
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px dashed var(--border-light); padding-bottom: 8px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 8px;">
|
||||||
<h4 style="margin: 0; color: var(--text-primary);">📋 1. Modelo & Conteúdo</h4>
|
<h4 style="margin: 0; color: var(--text-primary); font-size: 0.95rem; display: flex; align-items: center; gap: 6px;">
|
||||||
<button type="button" id="btnCartazResetTemplate" class="btn-music-option" style="padding: 2px 8px; font-size: 0.72rem; margin: 0;">Limpar</button>
|
<span>📋</span> 1. Foco & Estrutura Pedagógica
|
||||||
|
</h4>
|
||||||
|
<button type="button" id="btnCartazResetTemplate" class="btn-music-option" style="padding: 3px 10px; font-size: 0.72rem; margin: 0; border-color: rgba(239, 68, 68, 0.3); color: #f87171;">
|
||||||
|
🔄 Limpar Campos
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SELETOR DE TEMPLATES PRÉ-CONFIGURADOS -->
|
<!-- SELETOR DE TEMPLATES PRÉ-CONFIGURADOS -->
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Modelos Rápidos (Templates)</label>
|
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Modelos Rápidos Pedagógicos</label>
|
||||||
<select id="cartazTemplateSelect" class="obs-select" style="width: 100%; margin-top: 4px; font-weight: 600; color: var(--brand-pink, #db2777);">
|
<select id="cartazTemplateSelect" class="obs-select" style="width: 100%; margin-top: 4px; font-weight: 600; color: var(--brand-pink, #db2777); background: var(--bg-tertiary); border: 1px solid rgba(219, 39, 119, 0.3);">
|
||||||
<option value="">✨ Escolha um modelo pedagógico...</option>
|
<option value="">✨ Escolha um modelo pedagógico...</option>
|
||||||
<optgroup label="🌟 Educação Infantil (0 a 5 anos)">
|
<optgroup label="🌟 Educação Infantil (Rotina e Convivência)">
|
||||||
<option value="combinados">🌟 Combinados & Convivência da Turma</option>
|
<option value="combinados">🌟 Combinados & Convivência da Turma</option>
|
||||||
<option value="rotina">🍏 Rotina Diária & Lanche Saudável</option>
|
<option value="rotina">🍏 Rotina Diária & Lanche Saudável</option>
|
||||||
<option value="aniversarios">🎂 Mural dos Aniversariantes</option>
|
<option value="aniversarios">🎂 Mural dos Aniversariantes</option>
|
||||||
<option value="brinquedos">🧸 Organização dos Brinquedos e Cantos</option>
|
<option value="brinquedos">🧸 Organização dos Brinquedos e Cantinhos</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="📚 Campos de Experiência (BNCC)">
|
<optgroup label="📚 Campos de Experiência (BNCC)">
|
||||||
<option value="bncc_eu_outro">🤝 O Eu, o Outro e o Nós (EI02EO / EI03EO)</option>
|
<option value="bncc_eu_outro">🤝 O Eu, o Outro e o Nós (EI02EO / EI03EO)</option>
|
||||||
<option value="bncc_corpo">🏃 Corpo, Gestos e Movimentos (EI02CG / EI03CG)</option>
|
<option value="bncc_corpo">🏃 Corpo, Gestos e Movimentos (EI02CG / EI03CG)</option>
|
||||||
<option value="bncc_tracos">🎨 Traços, Sons, Cores e Formas (EI02TS / EI03TS)</option>
|
<option value="bncc_tracos">🎨 Traços, Sons, Cores e Formas (EI02TS / EI03TS)</option>
|
||||||
<option value="bncc_escuta">📖 Escuta, Fala e Imaginação (EI02EF / EI03EF)</option>
|
<option value="bncc_escuta">📖 Escuta, Fala, Pensamento e Imaginação (EI02EF)</option>
|
||||||
<option value="bncc_espacos">🔍 Espaços, Tempos e Relações (EI02ET / EI03ET)</option>
|
<option value="bncc_espacos">🔍 Espaços, Tempos, Quantidades e Relações (EI02ET)</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="🧠 Metodologia Mind Lab">
|
<optgroup label="🧠 Metodologia Mind Lab & Jogos">
|
||||||
<option value="mindlab_regras">♟️ Cantinho dos Jogos & Pensamento Estratégico</option>
|
<option value="mindlab_regras">♟️ Cantinho dos Jogos & Pensamento Estratégico</option>
|
||||||
<option value="mindlab_cooperacao">🧩 Desafios de Raciocínio & Trabalho em Equipe</option>
|
<option value="mindlab_cooperacao">🧩 Desafios de Raciocínio & Trabalho em Equipe</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="📢 Avisos & Famílias">
|
<optgroup label="📢 Avisos, Família & Comunidade">
|
||||||
<option value="aviso_pais">📌 Recado Importante para as Famílias</option>
|
<option value="aviso_pais">📌 Recado Importante para as Famílias</option>
|
||||||
<option value="mostra_cultural">🎉 Convite para Mostra Pedagógica & Cultural</option>
|
<option value="mostra_cultural">🎉 Convite para Mostra Pedagógica & Cultural</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- VINCULAR TURMA -->
|
<!-- VINCULAR TURMA E FORMATO -->
|
||||||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||||
<div class="settings-group" style="flex: 1; min-width: 140px;">
|
<div class="settings-group" style="flex: 1.2; min-width: 140px;">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Vincular à Turma</label>
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Turma Escolar</label>
|
||||||
<select id="cartazTurmaSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
<select id="cartazTurmaSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
||||||
<option value="">-- Selecionar Turma --</option>
|
<option value="">-- Selecionar Turma --</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-group" style="flex: 1; min-width: 140px;">
|
<div class="settings-group" style="flex: 1; min-width: 140px;">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Orientação</label>
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Formato / Papel</label>
|
||||||
<select id="cartazOrientacaoSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
<select id="cartazOrientacaoSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
||||||
<option value="retrato" selected>📄 Retrato (Vertical A4)</option>
|
<option value="retrato" selected>📄 Retrato (A4 Vertical)</option>
|
||||||
<option value="paisagem">📃 Paisagem (Horizontal A4)</option>
|
<option value="paisagem">📃 Paisagem (A4 Horizontal)</option>
|
||||||
|
<option value="quadrado">📱 Quadrado (1:1 WhatsApp/Post)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FAIXA ETÁRIA E CAMPO BNCC -->
|
||||||
|
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||||
|
<div class="settings-group" style="flex: 1; min-width: 140px;">
|
||||||
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Faixa Etária</label>
|
||||||
|
<select id="cartazFaixaEtaria" class="obs-select" style="width: 100%; margin-top: 4px;">
|
||||||
|
<option value="Bebês e Bem Pequenas (0 a 3 anos)">Berçário & Maternal (0 a 3 anos)</option>
|
||||||
|
<option value="Crianças Pequenas (4 a 5 anos e 11 meses)" selected>Pré-Escola (4 e 5 anos)</option>
|
||||||
|
<option value="Ensino Fundamental I">Ensino Fundamental (Anos Iniciais)</option>
|
||||||
|
<option value="Geral da Escola">Toda a Comunidade Escolar</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="settings-group" style="flex: 1; min-width: 140px;">
|
||||||
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Eixo BNCC / Tom</label>
|
||||||
|
<select id="cartazCampoBncc" class="obs-select" style="width: 100%; margin-top: 4px;">
|
||||||
|
<option value="Convivência e Interações">🤝 Convivência & Afeto</option>
|
||||||
|
<option value="Rotina e Autonomia">🍏 Rotina & Autonomia</option>
|
||||||
|
<option value="Linguagem e Oralidade">📖 Letramento & Histórias</option>
|
||||||
|
<option value="Corpo e Movimento">🏃 Movimento & Expressão</option>
|
||||||
|
<option value="Artes e Expressão">🎨 Arte, Sons e Cores</option>
|
||||||
|
<option value="Investigação da Natureza">🌱 Natureza & Ciências</option>
|
||||||
|
<option value="Avisos às Famílias">📌 Avisos & Comunicados</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CAMPOS EDITÁVEIS EM TEMPO REAL -->
|
<!-- CAMPOS EDITÁVEIS EM TEMPO REAL -->
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Título Principal</label>
|
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Título Principal do Cartaz</label>
|
||||||
<input type="text" id="cartazTituloInput" placeholder="Ex: Combinados da Nossa Turma" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
|
<input type="text" id="cartazTituloInput" placeholder="Ex: Combinados da Nossa Turma" class="obs-input" style="width: 100%; margin-top: 4px; padding: 9px; font-weight: 700; font-size: 0.95rem;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||||
|
<div class="settings-group" style="flex: 1.3; min-width: 160px;">
|
||||||
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Subtítulo / Descrição</label>
|
||||||
|
<input type="text" id="cartazSubtituloInput" placeholder="Ex: Educação Infantil • Professora Camila" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
|
||||||
|
</div>
|
||||||
|
<div class="settings-group" style="flex: 0.8; min-width: 110px;">
|
||||||
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Etiqueta / Badge</label>
|
||||||
|
<input type="text" id="cartazBadgeInput" placeholder="Ex: 🌟 BNCC EI02" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DESTAQUES / ITENS DO CARTAZ COM EMOJIS RÁPIDOS -->
|
||||||
|
<div class="settings-group">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Itens & Destaques (um por linha)</label>
|
||||||
|
<button type="button" id="btnCartazAddLine" style="background: none; border: none; color: #10b981; font-size: 0.72rem; font-weight: 700; cursor: pointer; padding: 2px 6px;">+ Adicionar Linha</button>
|
||||||
|
</div>
|
||||||
|
<textarea id="cartazCardsInput" placeholder="Digite os itens ou mensagens do cartaz (um por linha)..." class="obs-input" style="width: 100%; min-height: 90px; padding: 9px; margin-top: 4px; font-size: 0.84rem; resize: vertical; line-height: 1.4;"></textarea>
|
||||||
|
<!-- Atalhos rápidos de emojis -->
|
||||||
|
<div style="display: flex; gap: 4px; flex-wrap: wrap; margin-top: 4px; align-items: center;">
|
||||||
|
<span style="font-size: 0.68rem; color: var(--text-muted); margin-right: 4px;">Inserir:</span>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🤝" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🤝</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🌟" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🌟</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🧸" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🧸</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🧼" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🧼</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🍎" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🍎</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🎨" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🎨</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="📚" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">📚</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🎵" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🎵</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🌿" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🌿</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="🧩" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🧩</button>
|
||||||
|
<button type="button" class="btn-emoji-insert" data-emoji="💖" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">💖</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Subtítulo / Faixa Etária</label>
|
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Rodapé / Mensagem Institucional</label>
|
||||||
<input type="text" id="cartazSubtituloInput" placeholder="Ex: Educação Infantil • Professora Camila" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-group">
|
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Conteúdo / Destaques do Cartaz</label>
|
|
||||||
<textarea id="cartazCardsInput" placeholder="Digite os itens ou mensagens do cartaz (um por linha)..." class="obs-input" style="width: 100%; min-height: 80px; padding: 8px; margin-top: 4px; font-size: 0.82rem; resize: vertical;"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-group">
|
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Rodapé / Mensagem de Apoio</label>
|
|
||||||
<input type="text" id="cartazRodapeInput" placeholder="Ex: EMEI São Bernardo do Campo • Projeto 2026" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
|
<input type="text" id="cartazRodapeInput" placeholder="Ex: EMEI São Bernardo do Campo • Projeto 2026" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ESTILO, CORES E FONTES -->
|
<!-- ESTILO, CORES E FONTES -->
|
||||||
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 8px; margin-top: 4px;">🎨 2. Estilo e Cores</h4>
|
<h4 style="margin: 6px 0 0 0; color: var(--text-primary); font-size: 0.95rem; border-bottom: 1px solid var(--border-light); padding-bottom: 8px; display: flex; align-items: center; gap: 6px;">
|
||||||
|
<span>🎨</span> 2. Estilo Visual & Paletas
|
||||||
|
</h4>
|
||||||
|
|
||||||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||||
<div class="settings-group" style="flex: 1; min-width: 130px;">
|
<div class="settings-group" style="flex: 1; min-width: 130px;">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Fonte Lúdica</label>
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Tipografia</label>
|
||||||
<select id="cartazFontSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
<select id="cartazFontSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
||||||
<option value="'Fredoka', sans-serif" selected>🎈 Fredoka (Fofa/Infantil)</option>
|
<option value="'Fredoka', sans-serif" selected>🎈 Fredoka (Fofa/Infantil)</option>
|
||||||
<option value="'Outfit', sans-serif">⚡ Outfit (Moderna)</option>
|
<option value="'Outfit', sans-serif">⚡ Outfit (Moderna/Limpa)</option>
|
||||||
<option value="'Caveat', cursive">✍️ Caveat (Manuscrita)</option>
|
<option value="'Caveat', cursive">✍️ Caveat (Manuscrita)</option>
|
||||||
<option value="'Playfair Display', serif">📖 Playfair (Clássica)</option>
|
<option value="'Playfair Display', serif">📖 Playfair (Clássica)</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -2534,6 +2606,16 @@
|
|||||||
<option value="6px dashed" selected>Pontilhada Colorida</option>
|
<option value="6px dashed" selected>Pontilhada Colorida</option>
|
||||||
<option value="6px double">Linha Dupla Clássica</option>
|
<option value="6px double">Linha Dupla Clássica</option>
|
||||||
<option value="8px solid">Moldura de Gibi</option>
|
<option value="8px solid">Moldura de Gibi</option>
|
||||||
|
<option value="4px solid">Borda Fina Delicada</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="settings-group" style="flex: 1; min-width: 130px;">
|
||||||
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Estilo dos Cartões</label>
|
||||||
|
<select id="cartazCardStyleSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
|
||||||
|
<option value="glass" selected>Transparente Suave (Glass)</option>
|
||||||
|
<option value="solid">Branco Sólido com Sombra</option>
|
||||||
|
<option value="postit">Estilo Post-it Colorido</option>
|
||||||
|
<option value="minimal">Borda Minimalista</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2542,40 +2624,60 @@
|
|||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Paletas Pedagógicas</label>
|
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Paletas Pedagógicas</label>
|
||||||
<div style="display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap;">
|
<div style="display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap;">
|
||||||
<button type="button" class="btn-palette-preset" data-bg="#fff9eb" data-text="#2d3748" data-card="#ffffff" style="background: #fff9eb; color: #2d3748; padding: 4px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Tons Pastéis</button>
|
<button type="button" class="btn-palette-preset" data-bg="#fff9eb" data-text="#2d3748" data-card="#ffffff" style="background: #fff9eb; color: #2d3748; padding: 5px 9px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Tons Pastéis</button>
|
||||||
<button type="button" class="btn-palette-preset" data-bg="#f0fdf4" data-text="#166534" data-card="#ffffff" style="background: #f0fdf4; color: #166534; padding: 4px 8px; border: 1px solid #bbf7d0; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Jardim Verde</button>
|
<button type="button" class="btn-palette-preset" data-bg="#f0fdf4" data-text="#166534" data-card="#ffffff" style="background: #f0fdf4; color: #166534; padding: 5px 9px; border: 1px solid #bbf7d0; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Jardim Verde</button>
|
||||||
<button type="button" class="btn-palette-preset" data-bg="#eff6ff" data-text="#1e40af" data-card="#ffffff" style="background: #eff6ff; color: #1e40af; padding: 4px 8px; border: 1px solid #bfdbfe; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Céu Azul</button>
|
<button type="button" class="btn-palette-preset" data-bg="#eff6ff" data-text="#1e40af" data-card="#ffffff" style="background: #eff6ff; color: #1e40af; padding: 5px 9px; border: 1px solid #bfdbfe; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Céu Azul</button>
|
||||||
<button type="button" class="btn-palette-preset" data-bg="#fdf2f8" data-text="#9d174d" data-card="#ffffff" style="background: #fdf2f8; color: #9d174d; padding: 4px 8px; border: 1px solid #fbcfe8; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Rosa Lúdico</button>
|
<button type="button" class="btn-palette-preset" data-bg="#fdf2f8" data-text="#9d174d" data-card="#ffffff" style="background: #fdf2f8; color: #9d174d; padding: 5px 9px; border: 1px solid #fbcfe8; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Rosa Lúdico</button>
|
||||||
<button type="button" class="btn-palette-preset" data-bg="#1e293b" data-text="#f8fafc" data-card="#334155" style="background: #1e293b; color: #f8fafc; padding: 4px 8px; border: 1px solid #475569; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Lousa Escura</button>
|
<button type="button" class="btn-palette-preset" data-bg="#fef3c7" data-text="#92400e" data-card="#ffffff" style="background: #fef3c7; color: #92400e; padding: 5px 9px; border: 1px solid #fde68a; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Sol da Manhã</button>
|
||||||
|
<button type="button" class="btn-palette-preset" data-bg="#1e293b" data-text="#f8fafc" data-card="#334155" style="background: #1e293b; color: #f8fafc; padding: 5px 9px; border: 1px solid #475569; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Lousa Escura</button>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 12px; margin-top: 8px; align-items: center;">
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px;">
|
||||||
|
<label style="font-size: 0.75rem; color: var(--text-secondary);">Fundo:</label>
|
||||||
|
<input type="color" id="cartazBgColor" value="#fff9eb" style="width: 28px; height: 28px; border: 1px solid var(--border-light); border-radius: 6px; cursor: pointer; background: transparent;">
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px;">
|
||||||
|
<label style="font-size: 0.75rem; color: var(--text-secondary);">Texto:</label>
|
||||||
|
<input type="color" id="cartazTextColor" value="#2d3748" style="width: 28px; height: 28px; border: 1px solid var(--border-light); border-radius: 6px; cursor: pointer; background: transparent;">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ILUSTRAÇÃO POR IA -->
|
<!-- ILUSTRAÇÃO & IMAGEM (IA OU FOTO PRÓPRIA) -->
|
||||||
<div class="settings-group">
|
<div class="settings-group" style="background: rgba(255,255,255,0.02); border: 1px solid var(--border-light); border-radius: 10px; padding: 12px;">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Ilustração por IA (MiniMax T2I)</label>
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||||
<textarea id="cartazPromptImagem" placeholder="Ex: Ilustração 3D Pixar de crianças felizes brincando juntas na escola, cores vivas..." class="obs-input" style="width: 100%; min-height: 50px; padding: 6px; margin-top: 4px; font-size: 0.78rem; resize: vertical;"></textarea>
|
<label style="color: var(--text-primary); font-size: 0.78rem; font-weight: 700; text-transform: uppercase;">🖼️ Ilustração do Cartaz</label>
|
||||||
<div style="display: flex; gap: 8px; margin-top: 6px;">
|
<span style="font-size: 0.7rem; color: var(--text-muted);">IA Pixar ou Foto Própria</span>
|
||||||
<button type="button" id="btnGenerateCartazImage" class="btn-music-option" style="border-color: #8b5cf6; color: #a78bfa; background: rgba(139, 92, 246, 0.1); font-weight: 600; padding: 5px 10px; font-size: 0.78rem;">
|
</div>
|
||||||
<span>🪄 Gerar Ilustração com IA</span>
|
|
||||||
|
<textarea id="cartazPromptImagem" placeholder="Descreva a cena desejada (ex: Crianças de diferentes origens brincando juntas em roda no jardim florido)..." class="obs-input" style="width: 100%; min-height: 52px; padding: 7px; font-size: 0.8rem; resize: vertical;"></textarea>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap;">
|
||||||
|
<button type="button" id="btnGenerateCartazImage" class="btn-music-option" style="border-color: #8b5cf6; color: #a78bfa; background: rgba(139, 92, 246, 0.12); font-weight: 600; padding: 6px 12px; font-size: 0.8rem;">
|
||||||
|
<span>🪄 Gerar com IA</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="btnRemoveCartazImage" class="btn-music-option" style="color: var(--text-muted); font-size: 0.78rem; padding: 5px 10px;">
|
<label class="btn-music-option" style="border-color: #3b82f6; color: #60a5fa; background: rgba(59, 130, 246, 0.12); font-weight: 600; padding: 6px 12px; font-size: 0.8rem; cursor: pointer; margin: 0; display: inline-flex; align-items: center; gap: 4px;">
|
||||||
Remover Imagem
|
<span>📷 Usar Foto Própria</span>
|
||||||
|
<input type="file" id="cartazImageUploadInput" accept="image/*" style="display: none;">
|
||||||
|
</label>
|
||||||
|
<button type="button" id="btnRemoveCartazImage" class="btn-music-option" style="color: var(--text-muted); font-size: 0.78rem; padding: 6px 10px; margin-left: auto;">
|
||||||
|
🗑️ Remover
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- BOTÃO GERAR NOVO CARTAZ COM IA COMPLETA -->
|
<!-- BOTÃO GERAR NOVO CARTAZ COM IA COMPLETA -->
|
||||||
<button type="button" id="btnGenerateCartaz" style="width: 100%; padding: 11px; background: #10b981; color: white; border: none; border-radius: 10px; font-weight: 700; font-size: 0.95rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); transition: all 0.2s; margin-top: 4px;">
|
<button type="button" id="btnGenerateCartaz" style="width: 100%; padding: 13px; background: linear-gradient(135deg, #10b981, #059669); color: white; border: none; border-radius: 12px; font-weight: 700; font-size: 0.96rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.35); transition: all 0.2s; margin-top: 6px;">
|
||||||
<span>🤖 Redigir Conteúdo com PedagogIA</span>
|
<span>🤖 Redigir Conteúdo com PedagogIA</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div id="cartazGenerateLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.85rem; padding: 10px; background: rgba(255, 255, 255, 0.02); border-radius: 8px;">
|
<div id="cartazGenerateLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.85rem; padding: 12px; background: rgba(16, 185, 129, 0.05); border: 1px solid rgba(16, 185, 129, 0.2); border-radius: 8px;">
|
||||||
<div class="media-spinner" style="width: 18px; height: 18px; border-width: 2px; border-top-color: #10b981;"></div>
|
<div class="media-spinner" style="width: 18px; height: 18px; border-width: 2px; border-top-color: #10b981;"></div>
|
||||||
<span id="cartazLoaderText">Diagramando cartaz pedagógico...</span>
|
<span id="cartazLoaderText">Diagramando cartaz com inteligência pedagógica...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Lado Esquerdo: Painel de Assets para Canvas -->
|
<!-- Lado Esquerdo: Painel de Assets para Canvas (Modo Interativo) -->
|
||||||
<div id="cartazLeftCanvasPanel" style="display: none; flex: 1; min-width: 320px; flex-direction: column; gap: 16px; padding-right: 12px;">
|
<div id="cartazLeftCanvasPanel" style="display: none; flex: 1; min-width: 320px; flex-direction: column; gap: 16px; padding-right: 12px;">
|
||||||
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 8px;">🎨 Elementos do Canvas</h4>
|
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 8px;">🎨 Elementos do Canvas</h4>
|
||||||
|
|
||||||
@@ -2615,7 +2717,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="sceneDecomposeStatus" style="font-size: 0.78rem; color: var(--text-secondary); text-align: center;">Gerando camadas...</div>
|
<div id="sceneDecomposeStatus" style="font-size: 0.78rem; color: var(--text-secondary); text-align: center;">Gerando camadas...</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
@@ -2635,29 +2736,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Lado Direito: Preview & Editor de Código -->
|
<!-- Lado Direito: Preview & Suite de Exportação de Alta Resolução -->
|
||||||
<div class="cartaz-preview-panel" style="flex: 1.3; min-width: 360px; display: flex; flex-direction: column; gap: 14px;">
|
<div class="cartaz-preview-panel" style="flex: 1.4; min-width: 380px; display: flex; flex-direction: column; gap: 12px;">
|
||||||
<div class="cartaz-header-container" style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 8px; flex-wrap: wrap; gap: 8px;">
|
<div class="cartaz-header-container" style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 10px; flex-wrap: wrap; gap: 10px;">
|
||||||
<h4 style="margin: 0; color: var(--text-primary);">👁️ Visualização em Tempo Real</h4>
|
<div>
|
||||||
<div class="cartaz-header-actions" style="display: flex; gap: 6px; flex-wrap: wrap;">
|
<h4 style="margin: 0; color: var(--text-primary); font-size: 0.95rem; display: flex; align-items: center; gap: 6px;">
|
||||||
<button type="button" id="btnExportCartazPNG" class="btn-save-settings" style="background: #10b981; border: none; color: white; padding: 5px 10px; font-size: 0.75rem; font-weight: 700; border-radius: 6px; cursor: pointer;" title="Baixar imagem PNG de alta resolução">🖼️ Baixar PNG</button>
|
<span>👁️</span> Visualização do Cartaz
|
||||||
<button type="button" id="btnExportCartazPDF" class="btn-save-settings" style="background: #8b5cf6; border: none; color: white; padding: 5px 10px; font-size: 0.75rem; font-weight: 700; border-radius: 6px; cursor: pointer;" title="Baixar documento PDF pronto para impressão">📄 Baixar PDF</button>
|
</h4>
|
||||||
<button type="button" id="btnPrintCartaz" class="btn-save-settings" style="background: #3b82f6; border: none; color: white; padding: 5px 10px; font-size: 0.75rem; font-weight: 700; border-radius: 6px; cursor: pointer;">🖨️ Imprimir</button>
|
<span style="font-size: 0.72rem; color: var(--text-secondary);">Clique diretamente no texto para editar</span>
|
||||||
<button type="button" id="btnCanvasModeToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 8px; font-size: 0.75rem;">🎨 Canvas</button>
|
</div>
|
||||||
<button type="button" id="btnEditCartazToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 8px; font-size: 0.75rem;">✏️ HTML</button>
|
|
||||||
|
<!-- AÇÕES DE EXPORTAÇÃO E FERRAMENTAS -->
|
||||||
|
<div class="cartaz-header-actions" style="display: flex; gap: 6px; flex-wrap: wrap; align-items: center;">
|
||||||
|
<button type="button" id="btnCopyCartazClipboard" class="btn-save-settings" style="background: rgba(16, 185, 129, 0.15); border: 1px solid rgba(16, 185, 129, 0.4); color: #10b981; padding: 6px 10px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer;" title="Copiar imagem para colar direto no WhatsApp, Word ou Canva">
|
||||||
|
📋 Copiar Imagem
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnExportCartazPNG" class="btn-save-settings" style="background: #10b981; border: none; color: white; padding: 6px 11px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer; box-shadow: 0 2px 6px rgba(16, 185, 129, 0.3);" title="Baixar imagem PNG de alta resolução">
|
||||||
|
🖼️ PNG
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnExportCartazPDF" class="btn-save-settings" style="background: #8b5cf6; border: none; color: white; padding: 6px 11px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer; box-shadow: 0 2px 6px rgba(139, 92, 246, 0.3);" title="Baixar documento PDF pronto para impressão">
|
||||||
|
📄 PDF
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnPrintCartaz" class="btn-save-settings" style="background: #3b82f6; border: none; color: white; padding: 6px 11px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer;" title="Abrir janela de impressão">
|
||||||
|
🖨️ Imprimir
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnCanvasModeToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 6px 9px; font-size: 0.76rem; border-radius: 8px;" title="Alternar modo de design livre com arrastar e soltar">
|
||||||
|
🎨 Canvas
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnEditCartazToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 6px 9px; font-size: 0.76rem; border-radius: 8px;" title="Ver ou editar código HTML">
|
||||||
|
✏️ HTML
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Painel de Controle do Canvas (Apenas visível no Modo Canvas) -->
|
<!-- Painel de Controle do Canvas (Apenas visível no Modo Canvas) -->
|
||||||
<div id="canvasControlPanel" style="display: none; background: var(--bg-secondary); border: 1px solid var(--border-light); padding: 10px; border-radius: 8px; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 8px;">
|
<div id="canvasControlPanel" style="display: none; background: var(--bg-tertiary); border: 1px solid var(--border-light); padding: 10px 14px; border-radius: 10px; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 6px;">
|
||||||
<button id="btnCanvasAddText" class="btn-save-settings" style="background: #10a37f; color: white; border: none; padding: 4px 10px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;">➕ Texto</button>
|
<button id="btnCanvasAddText" class="btn-save-settings" style="background: #10a37f; color: white; border: none; padding: 5px 12px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;">➕ Texto</button>
|
||||||
<button id="btnCanvasAddCard" class="btn-save-settings" style="background: #a855f7; color: white; border: none; padding: 4px 10px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;">➕ Card</button>
|
<button id="btnCanvasAddCard" class="btn-save-settings" style="background: #a855f7; color: white; border: none; padding: 5px 12px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;">➕ Card</button>
|
||||||
|
|
||||||
<div style="height: 20px; width: 1px; background: var(--border-light);"></div>
|
<div style="height: 20px; width: 1px; background: var(--border-light);"></div>
|
||||||
|
|
||||||
<!-- Controles do Item Selecionado -->
|
<!-- Controles do Item Selecionado -->
|
||||||
<span style="font-size: 0.75rem; color: var(--text-secondary);">Item:</span>
|
<span style="font-size: 0.75rem; color: var(--text-secondary);">Item:</span>
|
||||||
<select id="canvasFontFamily" class="obs-select" style="padding: 2px 5px; font-size: 0.75rem; min-width: 90px; margin: 0;">
|
<select id="canvasFontFamily" class="obs-select" style="padding: 3px 6px; font-size: 0.75rem; min-width: 90px; margin: 0;">
|
||||||
<option value="Fredoka">Fredoka</option>
|
<option value="Fredoka">Fredoka</option>
|
||||||
<option value="Outfit">Outfit</option>
|
<option value="Outfit">Outfit</option>
|
||||||
<option value="Caveat">Caveat</option>
|
<option value="Caveat">Caveat</option>
|
||||||
@@ -2665,7 +2786,7 @@
|
|||||||
<option value="Arial">Arial</option>
|
<option value="Arial">Arial</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<input type="number" id="canvasFontSize" class="obs-input" style="width: 55px; padding: 2px 5px; font-size: 0.75rem; margin: 0;" min="8" max="72" value="16" title="Tamanho da fonte (px)">
|
<input type="number" id="canvasFontSize" class="obs-input" style="width: 55px; padding: 3px 6px; font-size: 0.75rem; margin: 0;" min="8" max="72" value="16" title="Tamanho da fonte (px)">
|
||||||
|
|
||||||
<div style="display: flex; align-items: center; gap: 4px;">
|
<div style="display: flex; align-items: center; gap: 4px;">
|
||||||
<label style="font-size: 0.72rem; color: var(--text-secondary);">Cor:</label>
|
<label style="font-size: 0.72rem; color: var(--text-secondary);">Cor:</label>
|
||||||
@@ -2688,45 +2809,56 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display: flex; gap: 4px; align-items: center;">
|
<div style="display: flex; gap: 4px; align-items: center;">
|
||||||
<button id="btnCanvasZIndexUp" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 2px 6px; font-size: 0.7rem; cursor: pointer;" title="Trazer para Frente">🔺</button>
|
<button id="btnCanvasZIndexUp" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 3px 7px; font-size: 0.7rem; cursor: pointer;" title="Trazer para Frente">🔺</button>
|
||||||
<button id="btnCanvasZIndexDown" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 2px 6px; font-size: 0.7rem; cursor: pointer;" title="Enviar para Trás">🔻</button>
|
<button id="btnCanvasZIndexDown" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 3px 7px; font-size: 0.7rem; cursor: pointer;" title="Enviar para Trás">🔻</button>
|
||||||
<button id="btnCanvasDelete" class="btn-save-settings" style="background: #ef4444; color: white; border: none; padding: 2px 8px; font-size: 0.7rem; font-weight: bold; border-radius: 4px; cursor: pointer;" title="Excluir Elemento">🗑️ Excluir</button>
|
<button id="btnCanvasDelete" class="btn-save-settings" style="background: #ef4444; color: white; border: none; padding: 3px 8px; font-size: 0.7rem; font-weight: bold; border-radius: 4px; cursor: pointer;" title="Excluir Elemento">🗑️</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Área de Edição do Conteúdo (Inicialmente Oculta) -->
|
<!-- Área de Edição de Código HTML (Opcional) -->
|
||||||
<div id="cartazEditArea" style="display: none; flex-direction: column; gap: 8px;">
|
<div id="cartazEditArea" style="display: none; flex-direction: column; gap: 8px;">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.78rem; font-weight: 600; text-transform: uppercase;">Editor HTML do Cartaz</label>
|
<label style="color: var(--text-secondary); font-size: 0.78rem; font-weight: 600; text-transform: uppercase;">Editor HTML Direto</label>
|
||||||
<textarea id="cartazHtmlEditor" class="obs-input" style="width: 100%; min-height: 120px; font-family: monospace; font-size: 0.85rem; padding: 10px;"></textarea>
|
<textarea id="cartazHtmlEditor" class="obs-input" style="width: 100%; min-height: 120px; font-family: monospace; font-size: 0.85rem; padding: 10px;"></textarea>
|
||||||
<button type="button" id="btnUpdateCartazPreview" style="padding: 6px 12px; align-self: flex-end; background: var(--border-light); border: 1px solid var(--border-light); border-radius: 6px; cursor: pointer; color: var(--text-primary); font-size: 0.8rem;">Atualizar Preview</button>
|
<button type="button" id="btnUpdateCartazPreview" style="padding: 6px 14px; align-self: flex-end; background: #10b981; border: none; border-radius: 6px; cursor: pointer; color: white; font-weight: 700; font-size: 0.8rem;">Atualizar Preview</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Caixa de Preview Imprimível -->
|
<!-- Caixa de Preview Imprimível em Escala Real -->
|
||||||
<div style="background: #2d3748; padding: 12px; border-radius: 12px; border: 1px solid var(--border-light); overflow: auto; display: flex; justify-content: center; align-items: center; flex: 1; min-height: 400px;">
|
<div id="cartazPreviewScrollContainer" style="background: #0f172a; padding: 24px; border-radius: 14px; border: 1px solid var(--border-light); overflow: auto; display: flex; justify-content: center; align-items: flex-start; flex: 1; min-height: 520px; max-height: 70vh;">
|
||||||
<div id="cartazPrintableArea" style="background: #fff9eb; color: #2d3748; padding: 30px; border-radius: 12px; width: 100%; max-width: 500px; box-sizing: border-box; box-shadow: 0 10px 25px rgba(0,0,0,0.3); transition: all 0.3s; min-height: 600px; display: flex; flex-direction: column; font-family: 'Fredoka', sans-serif;">
|
|
||||||
<h1 id="cartazPreviewTitle" style="text-align: center; margin-top: 0; font-size: 2.2rem; border-bottom: 2px solid; padding-bottom: 12px; margin-bottom: 20px;">Título do Cartaz</h1>
|
<div id="cartazPrintableArea" style="background: #fff9eb; color: #2d3748; padding: 32px 28px; border-radius: 12px; width: 100%; max-width: 500px; box-sizing: border-box; box-shadow: 0 15px 35px rgba(0,0,0,0.4); transition: all 0.3s; min-height: 620px; display: flex; flex-direction: column; font-family: 'Fredoka', sans-serif; position: relative;">
|
||||||
|
|
||||||
<!-- Área da Imagem IA no Cartaz -->
|
<!-- Selo / Badge Superior (Opcional) -->
|
||||||
<div id="cartazPreviewImageContainer" style="display: none; width: 100%; margin-bottom: 20px; border-radius: 8px; border: 2px solid rgba(0,0,0,0.1); background: rgba(0,0,0,0.02); text-align: center; overflow: hidden;">
|
<div id="cartazPreviewBadge" style="display: none; align-self: center; margin-bottom: 12px; background: rgba(0,0,0,0.06); border: 1px solid rgba(0,0,0,0.12); padding: 3px 12px; border-radius: 20px; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.4px;">
|
||||||
<img id="cartazPreviewImage" src="" style="max-width: 100%; max-height: 240px; object-fit: contain; display: inline-block; vertical-align: middle;">
|
🌟 BNCC • EDUCAÇÃO INFANTIL
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Título do Cartaz (Com suporte a edição direta no preview) -->
|
||||||
|
<h1 id="cartazPreviewTitle" contenteditable="true" spellcheck="false" style="text-align: center; margin-top: 0; font-size: 2.2rem; border-bottom: 2px solid; padding-bottom: 12px; margin-bottom: 18px; outline: none; transition: background 0.2s;" title="Clique para editar o título diretamente">Título do Cartaz</h1>
|
||||||
|
|
||||||
|
<!-- Área da Imagem no Cartaz -->
|
||||||
|
<div id="cartazPreviewImageContainer" style="display: none; width: 100%; margin-bottom: 18px; border-radius: 10px; border: 2px solid rgba(0,0,0,0.1); background: rgba(0,0,0,0.02); text-align: center; overflow: hidden; position: relative;">
|
||||||
|
<img id="cartazPreviewImage" src="" style="max-width: 100%; max-height: 250px; object-fit: contain; display: inline-block; vertical-align: middle;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Conteúdo de Texto do Cartaz -->
|
<!-- Conteúdo de Texto do Cartaz -->
|
||||||
<div id="cartazPreviewContent" style="font-size: 1.1rem; line-height: 1.6; flex: 1; display: flex; flex-direction: column; gap: 12px;">
|
<div id="cartazPreviewContent" style="font-size: 1.1rem; line-height: 1.6; flex: 1; display: flex; flex-direction: column; gap: 12px; outline: none;">
|
||||||
<p style="text-align: center; color: #718096; font-style: italic;">Clique em "Criar Cartaz Pedagógico" para que a assistente monte e estruture o cartaz ideal para você.</p>
|
<p style="text-align: center; color: #718096; font-style: italic; padding: 40px 10px;">
|
||||||
|
Clique em <strong>"Redigir Conteúdo com PedagogIA"</strong> ou escolha um <strong>Modelo Rápido</strong> ao lado para ver o cartaz diagramado instantaneamente.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Conteúdo 2: Histórico -->
|
<!-- Conteúdo 2: Histórico de Cartazes Salvos -->
|
||||||
<div id="panelCartazesHistorico" style="display: none; flex-direction: column; gap: 16px;">
|
<div id="panelCartazesHistorico" style="display: none; flex-direction: column; gap: 16px;">
|
||||||
<div id="cartazesHistoryContainer" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px;">
|
<div id="cartazesHistoryContainer" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px;">
|
||||||
<!-- Preenchido via JS -->
|
<!-- Preenchido via JS -->
|
||||||
</div>
|
</div>
|
||||||
<div id="cartazesNoData" style="text-align: center; color: var(--text-muted); font-style: italic; padding: 40px 0;">
|
<div id="cartazesNoData" style="text-align: center; color: var(--text-muted); font-style: italic; padding: 50px 0;">
|
||||||
Nenhum cartaz gerado ainda. Crie um acima!
|
Nenhum cartaz gerado ainda. Crie seu primeiro cartaz na aba acima!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+21
-19
@@ -3917,34 +3917,36 @@ code {
|
|||||||
|
|
||||||
/* Garante que o conteúdo de texto se ajuste */
|
/* Garante que o conteúdo de texto se ajuste */
|
||||||
#cartazPreviewContent {
|
#cartazPreviewContent {
|
||||||
font-size: 3.5cqw !important;
|
font-size: 3.2cqw !important;
|
||||||
line-height: 1.4 !important;
|
line-height: 1.4 !important;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 3cqw;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
gap: 2cqw;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cartazPreviewContent > div,
|
#cartazPreviewTitle:focus {
|
||||||
#cartazPreviewContent > p,
|
background: rgba(255, 255, 255, 0.25);
|
||||||
#cartazPreviewContent > ul {
|
outline: 2px dashed rgba(16, 185, 129, 0.8);
|
||||||
box-sizing: border-box !important;
|
border-radius: 8px;
|
||||||
padding: 3.5cqw !important;
|
|
||||||
border-radius: 2cqw !important;
|
|
||||||
font-size: 3cqw !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#cartazPreviewContent h2,
|
.btn-palette-preset {
|
||||||
#cartazPreviewContent h3 {
|
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||||
font-size: 4.5cqw !important;
|
|
||||||
margin-top: 0 !important;
|
|
||||||
margin-bottom: 2cqw !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#cartazPreviewContent li {
|
.btn-palette-preset:hover {
|
||||||
font-size: 2.8cqw !important;
|
transform: translateY(-2px) scale(1.05);
|
||||||
margin-bottom: 1.5cqw !important;
|
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-emoji-insert {
|
||||||
|
transition: transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-emoji-insert:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Título principal do cartaz */
|
/* Título principal do cartaz */
|
||||||
|
|||||||
@@ -2147,65 +2147,120 @@ app.delete('/api/poesias/:id', requireAuth, async (req, res) => {
|
|||||||
// ROTAS DO CRIAR CARTAZES
|
// ROTAS DO CRIAR CARTAZES
|
||||||
// ============================================================
|
// ============================================================
|
||||||
app.post('/api/cartazes/generate', requireAuth, async (req, res) => {
|
app.post('/api/cartazes/generate', requireAuth, async (req, res) => {
|
||||||
const { titulo, tema, corFundo, corTexto, layout, promptImagem, tamanho, orientacao } = req.body;
|
const {
|
||||||
|
titulo,
|
||||||
|
tema,
|
||||||
|
subtitulo,
|
||||||
|
rodape,
|
||||||
|
faixaEtaria,
|
||||||
|
campoBncc,
|
||||||
|
tomPedagogico,
|
||||||
|
turmaId,
|
||||||
|
corFundo,
|
||||||
|
corTexto,
|
||||||
|
layout,
|
||||||
|
promptImagem,
|
||||||
|
tamanho,
|
||||||
|
orientacao,
|
||||||
|
estiloCards
|
||||||
|
} = req.body;
|
||||||
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
|
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
|
||||||
|
|
||||||
if (!titulo || !tema || !layout) {
|
if (!titulo || !tema) {
|
||||||
return res.status(400).json({ error: 'Título, tema e layout são obrigatórios.' });
|
return res.status(400).json({ error: 'Título e tema/foco são obrigatórios.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const agentConfig = readAgentConfig();
|
const agentConfig = readAgentConfig();
|
||||||
const agentName = agentConfig.agentName || "PedagogIA";
|
const agentName = agentConfig.agentName || "PedagogIA";
|
||||||
|
|
||||||
// 1. Gerar Conteúdo do Cartaz
|
// Buscar contexto da turma se fornecido
|
||||||
const posterPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
|
let turmaContexto = '';
|
||||||
A professora Camila quer criar um Cartaz para sala de aula da Educação Infantil.
|
if (turmaId) {
|
||||||
|
try {
|
||||||
Detalhes do Cartaz:
|
const tRes = await dbPool.query('SELECT nome, ano_letivo, periodo FROM escola.turmas WHERE id = $1', [turmaId]);
|
||||||
- Título: "${titulo}"
|
if (tRes.rows.length > 0) {
|
||||||
- Tema / Foco Pedagógico: "${tema}"
|
const t = tRes.rows[0];
|
||||||
- Tipo de Cartaz: "${layout}" (educativo, informativo, conscientizacao, cultural)
|
turmaContexto = `Turma Escolar: "${t.nome}" (${t.ano_letivo || '2026'}, período ${t.periodo || 'integral'})`;
|
||||||
- Tamanho: "${tamanho || 'A4'}"
|
}
|
||||||
- Orientação: "${orientacao === 'paisagem' ? 'Paisagem/Horizontal' : 'Retrato/Vertical'}"
|
} catch (tErr) {
|
||||||
|
console.warn('[Cartazes] Aviso ao buscar turma:', tErr.message);
|
||||||
Por favor, gere o CONTEÚDO textual e estrutural do cartaz em HTML.
|
}
|
||||||
|
|
||||||
REGRAS DE IDIOMA E LOCALIZAÇÃO:
|
|
||||||
- O conteúdo gerado deve ser IMPRETERIVELMENTE em português do Brasil (pt-BR). Não use termos em outros idiomas de forma desnecessária.
|
|
||||||
|
|
||||||
REGRAS FUNDAMENTAIS E ESTÉTICAS DE DIAGRAMAÇÃO:
|
|
||||||
- O cartaz deve caber ESTRITAMENTE em UMA ÚNICA PÁGINA FÍSICA (Tamanho ${tamanho || 'A4'}, orientação ${orientacao === 'paisagem' ? 'Horizontal/Paisagem' : 'Vertical/Retrato'}). Não extrapole este espaço de forma alguma.
|
|
||||||
- Limite a quantidade de conteúdo: gere no máximo 2 ou 3 cards (divs de conteúdo) no total. Cada card deve ter pouquíssimo texto (no máximo 3 ou 4 itens curtos por card).
|
|
||||||
- Distribua a informação em CARDS (pequenos cartões ou blocos curtos) bem espaçados, parecidos com pôsteres infantis modernos, onde o texto NÃO ocupa uma folha corrida.
|
|
||||||
- Os textos devem ser extremamente curtos, diretos, visuais e fáceis de ler.
|
|
||||||
- Use emojis apropriados para crianças.
|
|
||||||
- Se for "educativo", estruture o conteúdo pedagógico de forma visual (ex: caixas para alfabeto, tabuada, regras gramaticais).
|
|
||||||
- Se for "informativo", destaque avisos gerais e orientações (ex: horários, cardápio, provas) em caixas curtas e legíveis.
|
|
||||||
- Se for "conscientizacao", use frases curtas de impacto sobre campanhas (ex: prevenção, saúde, coleta seletiva), acompanhadas de dicas em pequenos blocos.
|
|
||||||
- Se for "cultural", promova a atividade de lazer, esporte ou feira destacando o evento de forma animada e pontual.
|
|
||||||
- Considere a orientação ${orientacao === 'paisagem' ? 'Paisagem (horizontal)' : 'Retrato (vertical)'} ao organizar o fluxo: para paisagem, os cards devem ficar lado a lado (2 colunas horizontais); para retrato, eles se empilham verticalmente.
|
|
||||||
- Retorne apenas o formato HTML (use <div>, <p>, <ul>, <li>, <strong>, <span>). Não adicione <html> ou <body>, retorne apenas o conteúdo interno. Não inclua o título <h1> pois ele já é colocado no topo pela interface.`;
|
|
||||||
|
|
||||||
let conteudoHtml = '';
|
|
||||||
|
|
||||||
// Tentar MiniMax M3
|
|
||||||
try {
|
|
||||||
console.log('[Cartazes] Gerando conteúdo com MiniMax...');
|
|
||||||
const result = await callMinimax({
|
|
||||||
messages: [{ role: 'user', content: posterPrompt }],
|
|
||||||
temperature: 0.7,
|
|
||||||
max_tokens: 1500
|
|
||||||
});
|
|
||||||
conteudoHtml = result.text;
|
|
||||||
} catch (err) {
|
|
||||||
console.error('[Cartazes] Erro MiniMax:', err.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback OpenRouter
|
// 1. Gerar Conteúdo do Cartaz com Inteligência Pedagógica Avançada
|
||||||
if (!conteudoHtml && process.env.OPENROUTER_API_KEY) {
|
const posterPrompt = `Você é a ${agentName}, assistente pedagógica de excelência da professora Camila Martella Gasparini Reifonas, especialista em Educação Infantil e Diretrizes da BNCC.
|
||||||
|
A professora Camila está criando um Cartaz Pedagógico de alta qualidade para a sala de aula da Educação Infantil.
|
||||||
|
|
||||||
|
DIRETRIZES DO PEDIDO:
|
||||||
|
- Título do Cartaz: "${titulo}"
|
||||||
|
- Tema / Foco Pedagógico: "${tema}"
|
||||||
|
- Subtítulo Pretendido: "${subtitulo || ''}"
|
||||||
|
- Faixa Etária: "${faixaEtaria || 'Educação Infantil (2 a 5 anos)'}"
|
||||||
|
- Campo de Experiência / BNCC: "${campoBncc || 'Convivência e Aprendizagem'}"
|
||||||
|
- Tom Pedagógico: "${tomPedagogico || 'Acolhedor, afetuoso e lúdico'}"
|
||||||
|
${turmaContexto ? `- Contexto: ${turmaContexto}` : ''}
|
||||||
|
- Formato Físico: Tamanho ${tamanho || 'A4'}, Orientação ${orientacao === 'paisagem' ? 'Paisagem/Horizontal' : 'Retrato/Vertical'}
|
||||||
|
- Tipo de Layout: "${layout || 'educativo'}" (educativo, combinados, rotina, informativo, conscientizacao, cultural)
|
||||||
|
|
||||||
|
REGRAS DE CONTEÚDO E DIAGRAMAÇÃO:
|
||||||
|
1. IDIOMA: Português do Brasil (pt-BR) impecável, carinhoso, claro e adequado para crianças e famílias.
|
||||||
|
2. ESPAÇO FÍSICO: O cartaz deve caber perfeitamente em UMA ÚNICA PÁGINA. Portanto, seja sintético, impactante e visual.
|
||||||
|
3. ESTRUTURA DE CARDS:
|
||||||
|
- Gere entre 3 a 5 itens/cards principais.
|
||||||
|
- Cada card DEVE começar com um emoji temático atrativo (ex: 🤝, 🌟, 🧸, 🧼, 🍎, 🎨, 📚).
|
||||||
|
- Frases curtas, afirmativas, positivas e de fácil memorização pelas crianças.
|
||||||
|
4. RETORNO OBRIGATÓRIO EM FORMATO JSON:
|
||||||
|
Responda ESTRITAMENTE com um objeto JSON válido no seguinte formato:
|
||||||
|
{
|
||||||
|
"titulo": "Título refinado e encantador para o topo do cartaz",
|
||||||
|
"subtitulo": "Subtítulo explicativo ou pedagógico (ex: faixa etária / campo BNCC)",
|
||||||
|
"badge": "Tag ou selo curto (ex: 🌟 BNCC • EI02EO ou 💡 Dica da Semana)",
|
||||||
|
"cards": [
|
||||||
|
"🤝 Frase ou combinado 1 com emoji inicial",
|
||||||
|
"🧸 Frase ou combinado 2 com emoji inicial",
|
||||||
|
"🧼 Frase ou combinado 3 com emoji inicial",
|
||||||
|
"👂 Frase ou combinado 4 com emoji inicial"
|
||||||
|
],
|
||||||
|
"rodape": "Mensagem carinhosa de fechamento no rodapé",
|
||||||
|
"promptImagem": "Sugestão detalhada em inglês para ilustrar este cartaz em estilo 3D Pixar ou vetor infantil colorido, SEM NENHUM TEXTO ESCRITO"
|
||||||
|
}
|
||||||
|
Não inclua comentários nem texto fora do bloco JSON.`;
|
||||||
|
|
||||||
|
let generatedData = null;
|
||||||
|
let conteudoHtml = '';
|
||||||
|
|
||||||
|
// Tentar chamada com MiniMax M3 (ou fallback automático em callMinimax)
|
||||||
|
try {
|
||||||
|
console.log('[Cartazes] Gerando cartaz com IA pedagógica...');
|
||||||
|
const result = await callMinimax({
|
||||||
|
messages: [{ role: 'user', content: posterPrompt }],
|
||||||
|
temperature: 0.6,
|
||||||
|
max_tokens: 1500
|
||||||
|
});
|
||||||
|
|
||||||
|
const rawText = result.text.trim();
|
||||||
|
// Tentar extrair JSON
|
||||||
|
const jsonMatch = rawText.match(/\{[\s\S]*\}/);
|
||||||
|
if (jsonMatch) {
|
||||||
|
try {
|
||||||
|
generatedData = JSON.parse(jsonMatch[0]);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[Cartazes] Erro ao parsear JSON da IA, usando fallback:', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generatedData) {
|
||||||
|
conteudoHtml = rawText.replace(/```html|```/g, '').trim();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Cartazes] Erro ao chamar IA principal:', err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback OpenRouter se necessário
|
||||||
|
if (!generatedData && !conteudoHtml && process.env.OPENROUTER_API_KEY) {
|
||||||
try {
|
try {
|
||||||
console.log('[Cartazes] Fallback OpenRouter...');
|
console.log('[Cartazes] Acionando fallback OpenRouter...');
|
||||||
const r = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
const r = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -2215,50 +2270,86 @@ REGRAS FUNDAMENTAIS E ESTÉTICAS DE DIAGRAMAÇÃO:
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
|
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
|
||||||
messages: [{ role: 'user', content: posterPrompt }],
|
messages: [{ role: 'user', content: posterPrompt }],
|
||||||
temperature: 0.7,
|
temperature: 0.6,
|
||||||
max_tokens: 1500
|
max_tokens: 1500
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
const data = await r.json();
|
const data = await r.json();
|
||||||
conteudoHtml = data.choices?.[0]?.message?.content || '';
|
const raw = data.choices?.[0]?.message?.content || '';
|
||||||
|
const match = raw.match(/\{[\s\S]*\}/);
|
||||||
|
if (match) {
|
||||||
|
generatedData = JSON.parse(match[0]);
|
||||||
|
} else {
|
||||||
|
conteudoHtml = raw.replace(/```html|```/g, '').trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Cartazes] Erro OpenRouter:', err.message);
|
console.error('[Cartazes] Erro OpenRouter fallback:', err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Se temos JSON estruturado, montamos os campos e o HTML diagramado
|
||||||
|
const finalTitulo = generatedData?.titulo || titulo;
|
||||||
|
const finalSubtitulo = generatedData?.subtitulo || subtitulo || tema;
|
||||||
|
const finalBadge = generatedData?.badge || (campoBncc ? `📚 ${campoBncc}` : '');
|
||||||
|
const finalCards = Array.isArray(generatedData?.cards) && generatedData.cards.length > 0
|
||||||
|
? generatedData.cards
|
||||||
|
: [
|
||||||
|
`🌟 ${tema}`,
|
||||||
|
`🤝 Vivência lúdica e cooperativa em sala`,
|
||||||
|
`✨ Cuidamos e aprendemos juntos todos os dias`
|
||||||
|
];
|
||||||
|
const finalRodape = generatedData?.rodape || rodape || (turmaContexto ? `${turmaContexto} • Professora Camila` : 'Educando com afeto, empatia e compromisso');
|
||||||
|
const finalPromptSugerido = generatedData?.promptImagem || promptImagem || `Cute children learning happily about ${tema}, 3D Pixar style, colorful, warm lighting, no text`;
|
||||||
|
|
||||||
|
// Montar HTML estilizado caso a interface use conteúdo rico
|
||||||
if (!conteudoHtml) {
|
if (!conteudoHtml) {
|
||||||
conteudoHtml = `<h2>${titulo}</h2><p>Tema: ${tema}</p><p>Não foi possível gerar com IA. Edite o conteúdo livremente.</p>`;
|
const isLandscape = orientacao === 'paisagem';
|
||||||
|
const gridStyle = isLandscape
|
||||||
|
? 'display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; width: 100%;'
|
||||||
|
: 'display: flex; flex-direction: column; gap: 10px; width: 100%;';
|
||||||
|
|
||||||
|
const cardsHtml = finalCards.map(c => `
|
||||||
|
<div style="background: rgba(255, 255, 255, 0.45); backdrop-filter: blur(6px); border: 2px solid rgba(0, 0, 0, 0.08); border-radius: 12px; padding: 12px 16px; font-size: 1.05rem; font-weight: 600; display: flex; align-items: center; gap: 10px; box-shadow: 0 3px 8px rgba(0,0,0,0.04);">
|
||||||
|
<span>${c}</span>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
conteudoHtml = `
|
||||||
|
${finalSubtitulo ? `<div style="text-align: center; font-size: 1.15rem; font-weight: 600; opacity: 0.9; margin-bottom: 12px; letter-spacing: 0.3px;">${finalSubtitulo}</div>` : ''}
|
||||||
|
<div style="${gridStyle}">${cardsHtml}</div>
|
||||||
|
${finalRodape ? `<div style="margin-top: auto; padding-top: 14px; text-align: center; font-size: 0.85rem; font-weight: 600; opacity: 0.75; border-top: 1px dashed rgba(0,0,0,0.15);">${finalRodape}</div>` : ''}
|
||||||
|
`.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean HTML from backticks
|
// 2. Gerar Imagem Ilustrativa com IA se solicitado
|
||||||
conteudoHtml = conteudoHtml.replace(/```html|```/g, '').trim();
|
|
||||||
|
|
||||||
// 2. Gerar Imagem Ilustrativa se solicitado
|
|
||||||
let generatedImageUrl = null;
|
let generatedImageUrl = null;
|
||||||
if (promptImagem && promptImagem.trim().length > 3) {
|
const promptParaGerar = promptImagem?.trim() || (generatedData?.promptImagem ? generatedData.promptImagem : null);
|
||||||
|
|
||||||
|
if (promptParaGerar && promptParaGerar.length > 3) {
|
||||||
try {
|
try {
|
||||||
console.log('[Cartazes] Refinando prompt de imagem para inglês (sem texto)...');
|
console.log('[Cartazes] Refinando prompt de imagem para estilo infantil e garantindo ZERO texto...');
|
||||||
let refinedPrompt = promptImagem;
|
let refinedPrompt = promptParaGerar;
|
||||||
try {
|
try {
|
||||||
const refineRes = await callMinimax({
|
const refineRes = await callMinimax({
|
||||||
messages: [{
|
messages: [{
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: `Translate the following Portuguese description into a clean English description suitable for an image generation prompt.
|
content: `Translate and refine this image request into an ultra-clean English prompt for an image generator.
|
||||||
Make sure the prompt specifies a beautiful, colorful, kid-friendly vector/illustration style.
|
Description: "${promptParaGerar}"
|
||||||
CRITICAL REQUIREMENT: Explicitly specify in the prompt that there should be absolutely no text, no words, no signs, no letters, no labels, no speech bubbles, and no writing of any kind.
|
Subject context: Classroom poster for early childhood education (${tema}).
|
||||||
Portuguese description: "${promptImagem}"
|
Art style: Friendly 3D Pixar character cartoon, vibrant soft colors, high resolution.
|
||||||
Return ONLY the English prompt, no extra chat.`
|
CRITICAL SAFETY INSTRUCTION: There must be ABSOLUTELY NO TEXT, no letters, no words, no signs, no speech bubbles, and no captions anywhere in the image.
|
||||||
|
Return ONLY the refined English prompt.`
|
||||||
}],
|
}],
|
||||||
temperature: 0.3,
|
temperature: 0.2,
|
||||||
max_tokens: 250
|
max_tokens: 200
|
||||||
});
|
});
|
||||||
if (refineRes && refineRes.text) {
|
if (refineRes && refineRes.text) {
|
||||||
refinedPrompt = refineRes.text;
|
refinedPrompt = refineRes.text.replace(/["\n]/g, ' ').trim();
|
||||||
}
|
}
|
||||||
} catch (refineErr) {
|
} catch (refineErr) {
|
||||||
console.error('[Cartazes] Erro ao refinar prompt:', refineErr);
|
console.error('[Cartazes] Erro ao refinar prompt:', refineErr.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Cartazes] Gerando ilustração com prompt final:', refinedPrompt);
|
console.log('[Cartazes] Gerando ilustração com prompt final:', refinedPrompt);
|
||||||
@@ -2271,7 +2362,7 @@ Return ONLY the English prompt, no extra chat.`
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: 'image-01',
|
model: 'image-01',
|
||||||
prompt: `${refinedPrompt}. Complete figures shown from top to bottom without cutoffs, kid-friendly illustration style, 3D Pixar, soft pastel colors, no text, no words, no letters, no writing, no labels.`,
|
prompt: `${refinedPrompt}, complete joyful composition, children book illustration, 3D Pixar, soft pastel lighting, clean background, no text, no words, no letters, no watermark.`,
|
||||||
n: 1
|
n: 1
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -2293,9 +2384,12 @@ Return ONLY the English prompt, no extra chat.`
|
|||||||
generatedImageUrl = `/generated-media/${fileName}`;
|
generatedImageUrl = `/generated-media/${fileName}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const errBody = await genResp.text();
|
||||||
|
console.warn('[Cartazes] Falha na API de imagem MiniMax:', genResp.status, errBody);
|
||||||
}
|
}
|
||||||
} catch (imgErr) {
|
} catch (imgErr) {
|
||||||
console.error('[Cartazes] Erro na geração de imagem:', imgErr);
|
console.error('[Cartazes] Erro na geração de imagem:', imgErr.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2303,17 +2397,22 @@ Return ONLY the English prompt, no extra chat.`
|
|||||||
const dbRes = await dbPool.query(
|
const dbRes = await dbPool.query(
|
||||||
`INSERT INTO escola.cartazes (usuario_id, titulo, tema, cor_fundo, cor_texto, layout, conteudo, imagem_url)
|
`INSERT INTO escola.cartazes (usuario_id, titulo, tema, cor_fundo, cor_texto, layout, conteudo, imagem_url)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at`,
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at`,
|
||||||
[usuarioId, titulo, tema, corFundo || '#ffffff', corTexto || '#1e293b', layout, conteudoHtml, generatedImageUrl]
|
[usuarioId, finalTitulo, tema, corFundo || '#fff9eb', corTexto || '#2d3748', layout || 'educativo', conteudoHtml, generatedImageUrl]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
id: dbRes.rows[0].id,
|
id: dbRes.rows[0].id,
|
||||||
titulo,
|
titulo: finalTitulo,
|
||||||
|
subtitulo: finalSubtitulo,
|
||||||
|
badge: finalBadge,
|
||||||
|
cards: finalCards,
|
||||||
|
rodape: finalRodape,
|
||||||
|
promptImagem: finalPromptSugerido,
|
||||||
tema,
|
tema,
|
||||||
corFundo,
|
corFundo: corFundo || '#fff9eb',
|
||||||
corTexto,
|
corTexto: corTexto || '#2d3748',
|
||||||
layout,
|
layout: layout || 'educativo',
|
||||||
conteudo: conteudoHtml,
|
conteudo: conteudoHtml,
|
||||||
imagemUrl: generatedImageUrl,
|
imagemUrl: generatedImageUrl,
|
||||||
createdAt: dbRes.rows[0].created_at
|
createdAt: dbRes.rows[0].created_at
|
||||||
@@ -2325,6 +2424,44 @@ Return ONLY the English prompt, no extra chat.`
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Rota para upload direto de imagem/foto do professor para o cartaz
|
||||||
|
app.post('/api/cartazes/upload-image', requireAuth, async (req, res) => {
|
||||||
|
const { base64Data } = req.body;
|
||||||
|
if (!base64Data) {
|
||||||
|
return res.status(400).json({ error: 'Nenhuma imagem fornecida.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const matches = base64Data.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
|
||||||
|
if (!matches || matches.length !== 3) {
|
||||||
|
return res.status(400).json({ error: 'Formato de imagem base64 inválido.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBuffer = Buffer.from(matches[2], 'base64');
|
||||||
|
let fileExt = matches[1].split('/')[1] || 'png';
|
||||||
|
if (fileExt === 'jpeg') fileExt = 'jpg';
|
||||||
|
|
||||||
|
const mediaDir = path.join(__dirname, 'public', 'generated-media');
|
||||||
|
if (!fs.existsSync(mediaDir)) {
|
||||||
|
fs.mkdirSync(mediaDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = `cartaz_upload_${Date.now()}_${Math.floor(Math.random() * 1000)}.${fileExt}`;
|
||||||
|
const filePath = path.join(mediaDir, fileName);
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, imageBuffer);
|
||||||
|
await backupMediaFile(filePath, imageBuffer);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
imageUrl: `/generated-media/${fileName}`
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Cartazes Upload Image] Erro:', err);
|
||||||
|
res.status(500).json({ error: 'Falha ao salvar imagem enviada: ' + err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/api/cartazes/decompose-scene', requireAuth, async (req, res) => {
|
app.post('/api/cartazes/decompose-scene', requireAuth, async (req, res) => {
|
||||||
const { tema, titulo, layers_manual } = req.body;
|
const { tema, titulo, layers_manual } = req.body;
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ fi
|
|||||||
COOLIFY_RESOURCE_UUID="zzmj37ya8lmntomh3bm8rivi"
|
COOLIFY_RESOURCE_UUID="zzmj37ya8lmntomh3bm8rivi"
|
||||||
COOLIFY_TOKEN="12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03"
|
COOLIFY_TOKEN="12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03"
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🔄 Disparando Deploy na Coolify (camila.reifonas.cloud)...${NC}"
|
||||||
|
curl -s -X GET "https://painel.reifonas.cloud/api/v1/deploy?uuid=${COOLIFY_RESOURCE_UUID}&force=false" \
|
||||||
|
-H "Authorization: Bearer $COOLIFY_TOKEN"
|
||||||
|
|
||||||
echo -e "\n${GREEN}✅ Deploy engatilhado com sucesso!${NC}\n"
|
echo -e "\n${GREEN}✅ Deploy engatilhado com sucesso!${NC}\n"
|
||||||
|
|
||||||
# 3. Sincronização pós-deploy do .env no container em background
|
# 3. Sincronização pós-deploy do .env no container em background
|
||||||
|
|||||||
Reference in New Issue
Block a user