Spaces:
Running
Running
| <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Obramax Flyer Gen - Gerador de Flyers de Promoções</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> | |
| <style> | |
| :root { | |
| --primary: #e63946; | |
| --secondary: #f4a261; | |
| --accent: #2a9d8f; | |
| --dark: #1d3557; | |
| --light: #f1faee; | |
| --bg: #f8f9fa; | |
| --glass: rgba(255, 255, 255, 0.85); | |
| --shadow: 0 8px 32px rgba(0, 0, 0, 0.1); | |
| --radius: 16px; | |
| --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| min-height: 100vh; | |
| color: var(--dark); | |
| } | |
| /* Header */ | |
| .header { | |
| background: linear-gradient(90deg, var(--dark) 0%, #2c3e50 100%); | |
| color: white; | |
| padding: 1rem 2rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.15); | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| flex-wrap: wrap; | |
| gap: 1rem; | |
| } | |
| .header-brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| font-size: 1.5rem; | |
| font-weight: 800; | |
| letter-spacing: -0.5px; | |
| } | |
| .header-brand i { | |
| color: var(--secondary); | |
| font-size: 1.8rem; | |
| } | |
| .header-link { | |
| color: rgba(255,255,255,0.85); | |
| text-decoration: none; | |
| font-size: 0.85rem; | |
| font-weight: 500; | |
| padding: 6px 12px; | |
| border: 1px solid rgba(255,255,255,0.3); | |
| border-radius: 20px; | |
| transition: var(--transition); | |
| } | |
| .header-link:hover { | |
| background: rgba(255,255,255,0.15); | |
| color: white; | |
| transform: translateY(-1px); | |
| } | |
| /* Layout Principal */ | |
| .app-container { | |
| display: grid; | |
| grid-template-columns: 380px 1fr; | |
| max-width: 1400px; | |
| margin: 2rem auto; | |
| padding: 0 1.5rem; | |
| gap: 2rem; | |
| min-height: calc(100vh - 100px); | |
| } | |
| /* Painel de Controle */ | |
| .controls-panel { | |
| background: var(--glass); | |
| backdrop-filter: blur(12px); | |
| -webkit-backdrop-filter: blur(12px); | |
| border-radius: var(--radius); | |
| padding: 1.5rem; | |
| box-shadow: var(--shadow); | |
| border: 1px solid rgba(255,255,255,0.5); | |
| height: fit-content; | |
| position: sticky; | |
| top: 90px; | |
| overflow-y: auto; | |
| max-height: calc(100vh - 110px); | |
| } | |
| .panel-title { | |
| font-size: 1.25rem; | |
| font-weight: 700; | |
| margin-bottom: 1.5rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| color: var(--dark); | |
| padding-bottom: 0.75rem; | |
| border-bottom: 2px solid var(--secondary); | |
| } | |
| .form-group { | |
| margin-bottom: 1.25rem; | |
| } | |
| .form-group label { | |
| display: block; | |
| font-weight: 600; | |
| font-size: 0.85rem; | |
| margin-bottom: 0.5rem; | |
| color: var(--dark); | |
| opacity: 0.9; | |
| } | |
| .form-control { | |
| width: 100%; | |
| padding: 0.75rem 1rem; | |
| border: 2px solid #e2e8f0; | |
| border-radius: 10px; | |
| font-family: inherit; | |
| font-size: 0.9rem; | |
| transition: var(--transition); | |
| background: rgba(255,255,255,0.9); | |
| } | |
| .form-control:focus { | |
| outline: none; | |
| border-color: var(--accent); | |
| box-shadow: 0 0 0 3px rgba(42, 157, 143, 0.15); | |
| } | |
| input[type="color"].form-control { | |
| height: 42px; | |
| padding: 4px; | |
| cursor: pointer; | |
| } | |
| .color-row { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 1rem; | |
| } | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| padding: 0.75rem 1.25rem; | |
| border: none; | |
| border-radius: 10px; | |
| font-family: inherit; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, var(--primary), #d62839); | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(230, 57, 70, 0.3); | |
| width: 100%; | |
| margin-top: 0.5rem; | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(230, 57, 70, 0.4); | |
| } | |
| .btn-secondary { | |
| background: linear-gradient(135deg, var(--dark), #2c3e50); | |
| color: white; | |
| width: 100%; | |
| margin-top: 1rem; | |
| padding: 1rem; | |
| font-size: 1rem; | |
| } | |
| .btn-secondary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(29, 53, 87, 0.3); | |
| } | |
| .btn-small { | |
| padding: 0.4rem 0.8rem; | |
| font-size: 0.8rem; | |
| background: var(--primary); | |
| color: white; | |
| border-radius: 6px; | |
| } | |
| .btn-danger { | |
| background: #e63946; | |
| color: white; | |
| padding: 0.4rem; | |
| border-radius: 6px; | |
| width: 32px; | |
| height: 32px; | |
| } | |
| /* Lista de Produtos no Painel */ | |
| .products-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| margin-top: 1rem; | |
| } | |
| .product-editor { | |
| background: white; | |
| border: 2px solid #edf2f7; | |
| border-radius: 12px; | |
| padding: 1rem; | |
| position: relative; | |
| transition: var(--transition); | |
| } | |
| .product-editor:hover { | |
| border-color: var(--secondary); | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.05); | |
| } | |
| .product-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 0.75rem; | |
| } | |
| .product-header span { | |
| font-weight: 700; | |
| font-size: 0.85rem; | |
| color: var(--dark); | |
| } | |
| .product-fields { | |
| display: grid; | |
| gap: 0.5rem; | |
| } | |
| .layout-selector { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 0.75rem; | |
| margin-top: 0.5rem; | |
| } | |
| .layout-option { | |
| border: 2px solid #e2e8f0; | |
| border-radius: 10px; | |
| padding: 0.75rem; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| background: white; | |
| } | |
| .layout-option.active { | |
| border-color: var(--accent); | |
| background: rgba(42, 157, 143, 0.05); | |
| color: var(--accent); | |
| } | |
| .layout-option:hover:not(.active) { | |
| border-color: #cbd5e0; | |
| } | |
| .layout-option i { | |
| font-size: 1.25rem; | |
| margin-bottom: 4px; | |
| display: block; | |
| } | |
| .layout-option span { | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| } | |
| /* Preview Area */ | |
| .preview-area { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 1.5rem; | |
| } | |
| .preview-wrapper { | |
| background: white; | |
| padding: 2rem; | |
| border-radius: var(--radius); | |
| box-shadow: var(--shadow); | |
| width: 100%; | |
| max-width: 600px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: flex-start; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| /* O Flyer em si */ | |
| #flyer-canvas { | |
| width: 100%; | |
| max-width: 550px; | |
| aspect-ratio: 3/4; | |
| background: white; | |
| position: relative; | |
| overflow: hidden; | |
| box-shadow: 0 20px 60px rgba(0,0,0,0.15); | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* Flyer Header */ | |
| .flyer-header { | |
| padding: 2rem; | |
| text-align: center; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .flyer-header::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: radial-gradient(circle, rgba(255,255,255,0.2) 0%, transparent 60%); | |
| pointer-events: none; | |
| } | |
| .flyer-logo { | |
| font-size: 2rem; | |
| font-weight: 900; | |
| letter-spacing: 2px; | |
| margin-bottom: 0.5rem; | |
| position: relative; | |
| z-index: 1; | |
| text-transform: uppercase; | |
| } | |
| .flyer-title { | |
| font-size: 2.5rem; | |
| font-weight: 900; | |
| margin-bottom: 0.5rem; | |
| position: relative; | |
| z-index: 1; | |
| line-height: 1.1; | |
| text-transform: uppercase; | |
| letter-spacing: -1px; | |
| } | |
| .flyer-subtitle { | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| opacity: 0.95; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| /* Flyer Body */ | |
| .flyer-body { | |
| flex: 1; | |
| padding: 1.5rem; | |
| display: grid; | |
| gap: 1rem; | |
| align-content: start; | |
| overflow: hidden; | |
| } | |
| .flyer-body.grid-layout { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| .flyer-body.list-layout { | |
| grid-template-columns: 1fr; | |
| } | |
| /* Product Card */ | |
| .flyer-product { | |
| background: white; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.08); | |
| display: flex; | |
| flex-direction: column; | |
| position: relative; | |
| border: 2px solid transparent; | |
| transition: transform 0.2s; | |
| } | |
| .grid-layout .flyer-product { | |
| aspect-ratio: 1/1.2; | |
| } | |
| .list-layout .flyer-product { | |
| flex-direction: row; | |
| align-items: center; | |
| height: 120px; | |
| } | |
| .flyer-product:hover { | |
| transform: scale(1.02); | |
| } | |
| .product-badge { | |
| position: absolute; | |
| top: 8px; | |
| right: 8px; | |
| background: #e63946; | |
| color: white; | |
| font-weight: 800; | |
| font-size: 0.75rem; | |
| padding: 4px 8px; | |
| border-radius: 20px; | |
| z-index: 2; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.15); | |
| } | |
| .product-image { | |
| width: 100%; | |
| height: 60%; | |
| object-fit: cover; | |
| background: #f1f5f9; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #94a3b8; | |
| font-size: 2.5rem; | |
| } | |
| .list-layout .product-image { | |
| width: 120px; | |
| height: 100%; | |
| } | |
| .product-info { | |
| padding: 0.75rem; | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| } | |
| .product-name { | |
| font-weight: 700; | |
| font-size: 0.9rem; | |
| color: #1e293b; | |
| margin-bottom: 0.25rem; | |
| line-height: 1.2; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| } | |
| .list-layout .product-name { | |
| font-size: 1.1rem; | |
| -webkit-line-clamp: 1; | |
| } | |
| .prices { | |
| display: flex; | |
| align-items: baseline; | |
| gap: 0.5rem; | |
| flex-wrap: wrap; | |
| } | |
| .old-price { | |
| text-decoration: line-through; | |
| color: #94a3b8; | |
| font-size: 0.8rem; | |
| } | |
| .new-price { | |
| font-size: 1.3rem; | |
| font-weight: 900; | |
| color: #e63946; | |
| } | |
| .list-layout .new-price { | |
| font-size: 1.5rem; | |
| } | |
| .discount-tag { | |
| display: inline-block; | |
| background: #dcfce7; | |
| color: #166534; | |
| font-size: 0.7rem; | |
| font-weight: 700; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| } | |
| /* Flyer Footer */ | |
| .flyer-footer { | |
| padding: 1.25rem; | |
| text-align: center; | |
| font-weight: 700; | |
| color: white; | |
| font-size: 1rem; | |
| position: relative; | |
| } | |
| .flyer-footer::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.1' fill-rule='evenodd'%3E%3Ccircle cx='3' cy='3' r='3'/%3E%3Ccircle cx='13' cy='13' r='3'/%3E%3C/g%3E%3C/svg%3E"); | |
| opacity: 0.3; | |
| } | |
| .flyer-footer span { | |
| position: relative; | |
| z-index: 1; | |
| } | |
| /* Decorative elements */ | |
| .deco-circle { | |
| position: absolute; | |
| border-radius: 50%; | |
| opacity: 0.15; | |
| pointer-events: none; | |
| } | |
| /* Responsividade */ | |
| @media (max-width: 968px) { | |
| .app-container { | |
| grid-template-columns: 1fr; | |
| max-width: 700px; | |
| } | |
| .controls-panel { | |
| position: relative; | |
| top: 0; | |
| max-height: none; | |
| order: 2; | |
| } | |
| .preview-area { | |
| order: 1; | |
| } | |
| #flyer-canvas { | |
| max-width: 100%; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .header { | |
| padding: 1rem; | |
| } | |
| .header-brand { | |
| font-size: 1.2rem; | |
| } | |
| .preview-wrapper { | |
| padding: 1rem; | |
| } | |
| .flyer-title { | |
| font-size: 1.8rem; | |
| } | |
| .flyer-header { | |
| padding: 1.5rem; | |
| } | |
| .flyer-body { | |
| padding: 1rem; | |
| } | |
| } | |
| /* Loading e Toast */ | |
| .toast { | |
| position: fixed; | |
| bottom: 2rem; | |
| right: 2rem; | |
| background: var(--dark); | |
| color: white; | |
| padding: 1rem 1.5rem; | |
| border-radius: 12px; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.2); | |
| transform: translateY(150%); | |
| transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); | |
| z-index: 1000; | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| } | |
| .toast.show { | |
| transform: translateY(0); | |
| } | |
| .loading-overlay { | |
| position: absolute; | |
| inset: 0; | |
| background: rgba(255,255,255,0.9); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 50; | |
| border-radius: var(--radius); | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.3s; | |
| } | |
| .loading-overlay.active { | |
| opacity: 1; | |
| pointer-events: all; | |
| } | |
| .spinner { | |
| width: 40px; | |
| height: 40px; | |
| border: 4px solid #e2e8f0; | |
| border-top-color: var(--accent); | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| /* Scrollbar customizada */ | |
| .controls-panel::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .controls-panel::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .controls-panel::-webkit-scrollbar-thumb { | |
| background-color: #cbd5e0; | |
| border-radius: 20px; | |
| } | |
| /* Animations */ | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| } | |
| .animate-float { | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| .section-divider { | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, #e2e8f0, transparent); | |
| margin: 1.5rem 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="header"> | |
| <div class="header-brand"> | |
| <i class="fa-solid fa-bullhorn"></i> | |
| <span>Obramax Flyer Gen</span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="header-link"> | |
| <i class="fa-solid fa-code"></i> Built with anycoder | |
| </a> | |
| </header> | |
| <main class="app-container"> | |
| <!-- Painel de Controles --> | |
| <aside class="controls-panel"> | |
| <div class="panel-title"> | |
| <i class="fa-solid fa-sliders"></i> | |
| Configurações do Flyer | |
| </div> | |
| <div class="form-group"> | |
| <label>Título da Promoção</label> | |
| <input type="text" class="form-control" id="input-title" value="MEGA PROMOÇÃO" maxlength="25"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Subtítulo / Descrição</label> | |
| <input type="text" class="form-control" id="input-subtitle" value="Ofertas imperdíveis para você!" maxlength="40"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Validade da Oferta</label> | |
| <input type="text" class="form-control" id="input-validity" value="Válido até 30/12/2024" maxlength="30"> | |
| </div> | |
| <div class="color-row"> | |
| <div class="form-group" style="margin-bottom:0"> | |
| <label>Cor Principal</label> | |
| <input type="color" class="form-control" id="input-primary" value="#e63946"> | |
| </div> | |
| <div class="form-group" style="margin-bottom:0"> | |
| <label>Cor Secundária</label> | |
| <input type="color" class="form-control" id="input-secondary" value="#f4a261"> | |
| </div> | |
| </div> | |
| <div class="section-divider"></div> | |
| <div class="form-group"> | |
| <label>Layout dos Produtos</label> | |
| <div class="layout-selector"> | |
| <div class="layout-option active" data-layout="grid" onclick="setLayout('grid')"> | |
| <i class="fa-solid fa-border-all"></i> | |
| <span>Grade</span> | |
| </div> | |
| <div class="layout-option" data-layout="list" onclick="setLayout('list')"> | |
| <i class="fa-solid fa-list"></i> | |
| <span>Lista</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="section-divider"></div> | |
| <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:0.5rem"> | |
| <label style="margin:0">Produtos</label> | |
| <span style="font-size:0.8rem; color:#64748b" id="product-count">3/6</span> | |
| </div> | |
| <div class="products-list" id="products-list"> | |
| <!-- Produtos inseridos via JS --> | |
| </div> | |
| <button class="btn btn-primary" onclick="addProduct()" id="btn-add-product"> | |
| <i class="fa-solid fa-plus"></i> Adicionar Produto | |
| </button> | |
| <div class="section-divider"></div> | |
| <button class="btn btn-secondary" onclick="downloadFlyer()"> | |
| <i class="fa-solid fa-download"></i> Baixar Flyer (PNG) | |
| </button> | |
| </aside> | |
| <!-- Área de Preview --> | |
| <section class="preview-area"> | |
| <div class="preview-wrapper" id="preview-wrapper"> | |
| <div class="loading-overlay" id="loading-overlay"> | |
| <div class="spinner"></div> | |
| </div> | |
| <!-- O Flyer --> | |
| <div id="flyer-canvas"> | |
| <div class="flyer-header" id="flyer-header"> | |
| <div class="deco-circle animate-float" style="width:120px;height:120px;background:white;top:-20px;left:-20px;"></div> | |
| <div class="deco-circle" style="width:80px;height:80px;background:white;opacity:0.1;bottom:10px;right:20px;"></div> | |
| <div class="flyer-logo">obramax.com.br</div> | |
| <div class="flyer-title" id="flyer-title">MEGA PROMOÇÃO</div> | |
| <div class="flyer-subtitle" id="flyer-subtitle">Ofertas imperdíveis para você!</div> | |
| </div> | |
| <div class="flyer-body grid-layout" id="flyer-body"> | |
| <!-- Produtos no flyer --> | |
| </div> | |
| <div class="flyer-footer" id="flyer-footer"> | |
| <span><i class="fa-solid fa-clock" style="margin-right:6px"></i> <span id="flyer-validity">Válido até 30/12/2024</span></span> | |
| </div> | |
| </div> | |
| </div> | |
| <p style="color:#64748b; font-size:0.85rem; text-align:center; max-width:500px"> | |
| <i class="fa-solid fa-circle-info"></i> O preview atualiza automaticamente. Clique em "Baixar Flyer" para exportar em alta resolução. | |
| </p> | |
| </section> | |
| </main> | |
| <div class="toast" id="toast"> | |
| <i class="fa-solid fa-check-circle"></i> | |
| <span id="toast-message">Flyer baixado com sucesso!</span> | |
| </div> | |
| <script> | |
| // Estado da aplicação | |
| const state = { | |
| title: 'MEGA PROMOÇÃO', | |
| subtitle: 'Ofertas imperdíveis para você!', | |
| validity: 'Válido até 30/12/2024', | |
| primary: '#e63946', | |
| secondary: '#f4a261', | |
| layout: 'grid', | |
| products: [ | |
| { id: 1, name: 'Cimento CP II 50kg', oldPrice: 'R$ 39,90', newPrice: 'R$ 29,90', image: 'https://images.unsplash.com/photo-1530124566582-a618bc2615dc?w=400&h=400&fit=crop' }, | |
| { id: 2, name: 'Tinta Acrílica 18L', oldPrice: 'R$ 189,00', newPrice: 'R$ 149,00', image: 'https://images.unsplash.com/photo-1589939705384-5185137a7f0f?w=400&h=400&fit=crop' }, | |
| { id: 3, name: 'Furadeira 650W', oldPrice: 'R$ 259,00', newPrice: 'R$ 199,00', image: 'https://images.unsplash.com/photo-1504148455328-c376907d081c?w=400&h=400&fit=crop' } | |
| ] | |
| }; | |
| let nextId = 4; | |
| // Referências DOM | |
| const els = { | |
| inputTitle: document.getElementById('input-title'), | |
| inputSubtitle: document.getElementById('input-subtitle'), | |
| inputValidity: document.getElementById('input-validity'), | |
| inputPrimary: document.getElementById('input-primary'), | |
| inputSecondary: document.getElementById('input-secondary'), | |
| productsList: document.getElementById('products-list'), | |
| flyerTitle: document.getElementById('flyer-title'), | |
| flyerSubtitle: document.getElementById('flyer-subtitle'), | |
| flyerValidity: document.getElementById('flyer-validity'), | |
| flyerBody: document.getElementById('flyer-body'), | |
| flyerHeader: document.getElementById('flyer-header'), | |
| flyerFooter: document.getElementById('flyer-footer'), | |
| toast: document.getElementById('toast'), | |
| toastMsg: document.getElementById('toast-message'), | |
| loading: document.getElementById('loading-overlay'), | |
| productCount: document.getElementById('product-count'), | |
| btnAdd: document.getElementById('btn-add-product') | |
| }; | |
| // Inicialização | |
| function init() { | |
| bindEvents(); | |
| renderProductEditors(); | |
| updateFlyer(); | |
| } | |
| function bindEvents() { | |
| els.inputTitle.addEventListener('input', (e) => { state.title = e.target.value; updateFlyer(); }); | |
| els.inputSubtitle.addEventListener('input', (e) => { state.subtitle = e.target.value; updateFlyer(); }); | |
| els.inputValidity.addEventListener('input', (e) => { state.validity = e.target.value; updateFlyer(); }); | |
| els.inputPrimary.addEventListener('input', (e) => { state.primary = e.target.value; updateFlyer(); }); | |
| els.inputSecondary.addEventListener('input', (e) => { state.secondary = e.target.value; updateFlyer(); }); | |
| } | |
| function setLayout(layout) { | |
| state.layout = layout; | |
| document.querySelectorAll('.layout-option').forEach(el => { | |
| el.classList.toggle('active', el.dataset.layout === layout); | |
| }); | |
| updateFlyer(); | |
| } | |
| function addProduct() { | |
| if (state.products.length >= 6) { | |
| showToast('Limite máximo de 6 produtos atingido!', 'warning'); | |
| return; | |
| } | |
| state.products.push({ | |
| id: nextId++, | |
| name: 'Novo Produto', | |
| oldPrice: 'R$ 0,00', | |
| newPrice: 'R$ 0,00', | |
| image: '' | |
| }); | |
| renderProductEditors(); | |
| updateFlyer(); | |
| } | |
| function removeProduct(id) { | |
| if (state.products.length <= 1) { | |
| showToast('Adicione pelo menos 1 produto!', 'warning'); | |
| return; | |
| } | |
| state.products = state.products.filter(p => p.id !== id); | |
| renderProductEditors(); | |
| updateFlyer(); | |
| } | |
| function updateProduct(id, field, value) { | |
| const product = state.products.find(p => p.id === id); | |
| if (product) { | |
| product[field] = value; | |
| updateFlyer(); | |
| } | |
| } | |
| function renderProductEditors() { | |
| els.productsList.innerHTML = ''; | |
| els.productCount.textContent = `${state.products.length}/6`; | |
| els.btnAdd.disabled = state.products.length >= 6; | |
| els.btnAdd.style.opacity = state.products.length >= 6 ? '0.5' : '1'; | |
| state.products.forEach((p, index) => { | |
| const div = document.createElement('div'); | |
| div.className = 'product-editor'; | |
| div.innerHTML = ` | |
| <div class="product-header"> | |
| <span><i class="fa-solid fa-tag" style="color:var(--secondary); margin-right:4px"></i> Produto ${index + 1}</span> | |
| <button class="btn btn-danger" onclick="removeProduct(${p.id})" title="Remover"> | |
| <i class="fa-solid fa-trash"></i> | |
| </button> | |
| </div> | |
| <div class="product-fields"> | |
| <input type="text" class="form-control" placeholder="Nome do produto" value="${p.name}" | |
| oninput="updateProduct(${p.id}, 'name', this.value)" maxlength="30"> | |
| <div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.5rem"> | |
| <input type="text" class="form-control" placeholder="Preço antigo" value="${p.oldPrice}" | |
| oninput="updateProduct(${p.id}, 'oldPrice', this.value)"> | |
| <input type="text" class="form-control" placeholder="Preço novo" value="${p.newPrice}" | |
| oninput="updateProduct(${p.id}, 'newPrice', this.value)"> | |
| </div> | |
| <input type="text" class="form-control" placeholder="URL da imagem (opcional)" value="${p.image}" | |
| oninput="updateProduct(${p.id}, 'image', this.value)"> | |
| </div> | |
| `; | |
| els.productsList.appendChild(div); | |
| }); | |
| } | |
| function calcDiscount(oldP, newP) { | |
| try { | |
| const oldVal = parseFloat(oldP.replace(/[^0-9,]/g, '').replace(',', '.')); | |
| const newVal = parseFloat(newP.replace(/[^0-9,]/g, '').replace(',', '.')); | |
| if (oldVal && newVal && oldVal > newVal) { | |
| return Math.round((1 - newVal / oldVal) * 100) + '% OFF'; | |
| } | |
| } catch(e) {} | |
| return ''; | |
| } | |
| function updateFlyer() { | |
| // Atualizar textos | |
| els.flyerTitle.textContent = state.title || 'PROMOÇÃO'; | |
| els.flyerSubtitle.textContent = state.subtitle; | |
| els.flyerValidity.textContent = state.validity; | |
| // Aplicar cores | |
| els.flyerHeader.style.background = `linear-gradient(135deg, ${state.primary}, ${state.secondary})`; | |
| els.flyerHeader.style.color = 'white'; | |
| els.flyerFooter.style.background = state.dark || '#1d3557'; | |
| els.flyerFooter.style.background = `linear-gradient(90deg, ${state.primary}, ${adjustColor(state.primary, -20)})`; | |
| // Layout | |
| els.flyerBody.className = `flyer-body ${state.layout}-layout`; | |
| // Renderizar produtos no flyer | |
| els.flyerBody.innerHTML = state.products.map(p => { | |
| const discount = calcDiscount(p.oldPrice, p.newPrice); | |
| const imgContent = p.image | |
| ? `<img src="${p.image}" class="product-image" alt="" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> | |
| <div class="product-image" style="display:none"><i class="fa-solid fa-box-open"></i></div>` | |
| : `<div class="product-image"><i class="fa-solid fa-box-open"></i></div>`; | |
| return ` | |
| <div class="flyer-product"> | |
| ${discount ? `<div class="product-badge">${discount}</div>` : ''} | |
| ${imgContent} | |
| <div class="product-info"> | |
| <div class="product-name">${p.name}</div> | |
| <div class="prices"> | |
| <span class="old-price">${p.oldPrice}</span> | |
| <span class="new-price">${p.newPrice}</span> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }).join(''); | |
| } | |
| function adjustColor(hex, amount) { | |
| // Função simples para escurecer/clarear cor hex | |
| let color = hex.replace('#', ''); | |
| if (color.length === 3) color = color.split('').map(c => c+c).join(''); | |
| const num = parseInt(color, 16); | |
| let r = (num >> 16) + amount; | |
| let g = ((num >> 8) & 0x00FF) + amount; | |
| let b = (num & 0x00FF) + amount; | |
| r = r < 0 ? 0 : r > 255 ? 255 : r; | |
| g = g < 0 ? 0 : g > 255 ? 255 : g; | |
| b = b < 0 ? 0 : b > 255 ? 255 : b; | |
| return `rgb(${r},${g},${b})`; | |
| } | |
| async function downloadFlyer() { | |
| els.loading.classList.add('active'); | |
| try { | |
| const canvas = await html2canvas(document.getElementById('flyer-canvas'), { | |
| scale: 2, | |
| useCORS: true, | |
| allowTaint: true, | |
| backgroundColor: '#ffffff', | |
| logging: false | |
| }); | |
| const link = document.createElement('a'); | |
| link.download = `obramax-flyer-${Date.now()}.png`; | |
| link.href = canvas.toDataURL('image/png'); | |
| link.click(); | |
| showToast('Flyer baixado com sucesso!'); | |
| } catch (err) { | |
| console.error(err); | |
| showToast('Erro ao gerar imagem. Tente novamente.', 'error'); | |
| } finally { | |
| setTimeout(() => els.loading.classList.remove('active'), 500); | |
| } | |
| } | |
| function showToast(msg, type = 'success') { | |
| els.toastMsg.textContent = msg; | |
| const icon = els.toast.querySelector('i'); | |
| if (type === 'error') { | |
| icon.className = 'fa-solid fa-circle-xmark'; | |
| els.toast.style.background = '#e63946'; | |
| } else if (type === 'warning') { | |
| icon.className = 'fa-solid fa-triangle-exclamation'; | |
| els.toast.style.background = '#f4a261'; | |
| } else { | |
| icon.className = 'fa-solid fa-check-circle'; | |
| els.toast.style.background = '#1d3557'; | |
| } | |
| els.toast.classList.add('show'); | |
| setTimeout(() => els.toast.classList.remove('show'), 3000); | |
| } | |
| // Iniciar app | |
| init(); | |
| </script> | |
| </body> | |
| </html> |