Spaces:
Running
Running
| <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Agenda Integrada | Gestão de Compromissos</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: { | |
| 50: '#f0f9ff', | |
| 100: '#e0f2fe', | |
| 200: '#bae6fd', | |
| 300: '#7dd3fc', | |
| 400: '#38bdf8', | |
| 500: '#0ea5e9', | |
| 600: '#0284c7', | |
| 700: '#0369a1', | |
| 800: '#075985', | |
| 900: '#0c4a6e', | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| /* Custom styles */ | |
| :root { | |
| --primary-color: #0ea5e9; | |
| --secondary-color: #7dd3fc; | |
| } | |
| /* Smooth transitions */ | |
| * { | |
| transition: background-color 0.2s ease, color 0.2s ease; | |
| } | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| border-radius: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #cbd5e1; | |
| border-radius: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #94a3b8; | |
| } | |
| /* Animations */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-out forwards; | |
| } | |
| .pulse { | |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| } | |
| /* Color picker */ | |
| .color-picker { | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| appearance: none; | |
| width: 30px; | |
| height: 30px; | |
| border: none; | |
| cursor: pointer; | |
| border-radius: 50%; | |
| padding: 0; | |
| background: transparent; | |
| } | |
| .color-picker::-webkit-color-swatch { | |
| border-radius: 50%; | |
| border: 2px solid #fff; | |
| box-shadow: 0 0 0 1px rgba(0,0,0,0.1); | |
| } | |
| /* Custom checkbox */ | |
| .custom-checkbox { | |
| position: relative; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid #cbd5e1; | |
| border-radius: 4px; | |
| appearance: none; | |
| cursor: pointer; | |
| } | |
| .custom-checkbox:checked { | |
| background-color: var(--primary-color); | |
| border-color: var(--primary-color); | |
| } | |
| .custom-checkbox:checked::after { | |
| content: ''; | |
| position: absolute; | |
| left: 6px; | |
| top: 2px; | |
| width: 5px; | |
| height: 10px; | |
| border: solid white; | |
| border-width: 0 2px 2px 0; | |
| transform: rotate(45deg); | |
| } | |
| /* Tooltip */ | |
| .tooltip { | |
| position: relative; | |
| } | |
| .tooltip:hover .tooltip-text { | |
| visibility: visible; | |
| opacity: 1; | |
| } | |
| .tooltip-text { | |
| visibility: hidden; | |
| opacity: 0; | |
| position: absolute; | |
| z-index: 10; | |
| bottom: 125%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background-color: #334155; | |
| color: white; | |
| text-align: center; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| white-space: nowrap; | |
| transition: all 0.2s ease; | |
| } | |
| .tooltip-text::after { | |
| content: ""; | |
| position: absolute; | |
| top: 100%; | |
| left: 50%; | |
| margin-left: -5px; | |
| border-width: 5px; | |
| border-style: solid; | |
| border-color: #334155 transparent transparent transparent; | |
| } | |
| /* Loading spinner */ | |
| .spinner { | |
| width: 24px; | |
| height: 24px; | |
| border: 3px solid rgba(255,255,255,0.3); | |
| border-radius: 50%; | |
| border-top-color: white; | |
| animation: spin 1s ease-in-out infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-slate-50 font-sans antialiased"> | |
| <div class="min-h-screen"> | |
| <!-- Header --> | |
| <header class="bg-gradient-to-r from-primary-600 to-primary-800 text-white shadow-lg"> | |
| <div class="container mx-auto px-4 py-6"> | |
| <div class="flex justify-between items-center"> | |
| <div> | |
| <h1 class="text-2xl md:text-3xl font-bold">Agenda Integrada</h1> | |
| <p class="text-primary-200">Gestão completa de seus compromissos</p> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button id="syncBtn" class="p-2 rounded-full hover:bg-primary-700 transition-colors tooltip"> | |
| <i class="fas fa-sync-alt"></i> | |
| <span class="tooltip-text">Sincronizar dados</span> | |
| </button> | |
| <div class="relative"> | |
| <button id="userMenuBtn" class="w-10 h-10 rounded-full bg-primary-500 flex items-center justify-center hover:bg-primary-400 transition-colors"> | |
| <i class="fas fa-user"></i> | |
| </button> | |
| <div id="userMenu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-20"> | |
| <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Meu perfil</a> | |
| <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Configurações</a> | |
| <div class="border-t border-gray-200 my-1"></div> | |
| <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Sair</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main content --> | |
| <main class="container mx-auto px-4 py-8 max-w-7xl"> | |
| <!-- Quick stats --> | |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8"> | |
| <div class="bg-white rounded-lg shadow p-4 flex items-center"> | |
| <div class="p-3 rounded-full bg-primary-100 text-primary-600 mr-4"> | |
| <i class="fas fa-calendar-day text-lg"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500">Hoje</p> | |
| <p id="todayDate" class="text-xl font-semibold">-</p> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4 flex items-center"> | |
| <div class="p-3 rounded-full bg-green-100 text-green-600 mr-4"> | |
| <i class="fas fa-calendar-check text-lg"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500">Compromissos</p> | |
| <p id="appointmentsCount" class="text-xl font-semibold">0</p> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4 flex items-center"> | |
| <div class="p-3 rounded-full bg-red-100 text-red-600 mr-4"> | |
| <i class="fas fa-calendar-times text-lg"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500">Datas Bloqueadas</p> | |
| <p id="blocksCount" class="text-xl font-semibold">0</p> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4 flex items-center"> | |
| <div class="p-3 rounded-full bg-amber-100 text-amber-600 mr-4"> | |
| <i class="fas fa-dollar-sign text-lg"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500">Total em Cache</p> | |
| <p id="totalFee" class="text-xl font-semibold">R$ 0,00</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex flex-col lg:flex-row gap-6"> | |
| <!-- Left sidebar - Add new event --> | |
| <div class="w-full lg:w-1/3 bg-white rounded-lg shadow-md p-6 h-fit sticky top-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-primary-800"> | |
| <i class="fas fa-plus-circle mr-2"></i>Novo Evento | |
| </h2> | |
| <button id="clearFormBtn" class="text-sm text-primary-600 hover:text-primary-800"> | |
| <i class="fas fa-times mr-1"></i>Limpar | |
| </button> | |
| </div> | |
| <form id="eventForm" class="space-y-4"> | |
| <div> | |
| <label class="block text-gray-700 mb-2 font-medium">Tipo de Evento</label> | |
| <div class="flex space-x-4"> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="eventType" value="appointment" checked | |
| class="form-radio text-primary-600 focus:ring-primary-500"> | |
| <span class="ml-2">Compromisso</span> | |
| </label> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="eventType" value="block" | |
| class="form-radio text-primary-600 focus:ring-primary-500"> | |
| <span class="ml-2">Bloquear Data</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="eventDate" class="block text-gray-700 mb-2 font-medium">Data</label> | |
| <input type="date" id="eventDate" required | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| </div> | |
| <div id="companySection"> | |
| <label for="companySelect" class="block text-gray-700 mb-2 font-medium">Empresa</label> | |
| <div class="flex space-x-2"> | |
| <select id="companySelect" | |
| class="flex-grow px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| <option value="">Selecione uma empresa</option> | |
| <!-- Options will be populated by JS --> | |
| </select> | |
| <button type="button" id="addCompanyBtn" | |
| class="px-3 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors tooltip"> | |
| <i class="fas fa-plus"></i> | |
| <span class="tooltip-text">Adicionar empresa</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="newCompanySection" class="hidden space-y-4"> | |
| <div> | |
| <label for="newCompanyName" class="block text-gray-700 mb-2 font-medium">Nova Empresa</label> | |
| <input type="text" id="newCompanyName" placeholder="Nome da empresa" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| </div> | |
| <div class="flex items-center"> | |
| <label for="companyColor" class="block text-gray-700 mr-3 font-medium">Cor:</label> | |
| <input type="color" id="companyColor" value="#0ea5e9" class="color-picker"> | |
| <button type="button" id="cancelAddCompany" | |
| class="ml-4 px-3 py-1 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"> | |
| Cancelar | |
| </button> | |
| </div> | |
| </div> | |
| <div id="descriptionSection"> | |
| <label for="eventDescription" class="block text-gray-700 mb-2 font-medium">Descrição</label> | |
| <textarea id="eventDescription" rows="3" placeholder="Detalhes do compromisso" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"></textarea> | |
| </div> | |
| <div id="feeSection"> | |
| <label for="eventFee" class="block text-gray-700 mb-2 font-medium">Valor do Cache (R$)</label> | |
| <div class="relative"> | |
| <span class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">R$</span> | |
| <input type="number" id="eventFee" min="0" step="0.01" placeholder="0,00" | |
| class="w-full pl-8 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| </div> | |
| </div> | |
| <div id="reminderSection" class="hidden"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" id="setReminder" class="custom-checkbox"> | |
| <span class="text-gray-700">Definir lembrete</span> | |
| </label> | |
| <div id="reminderOptions" class="hidden mt-2 pl-6 space-y-2"> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="reminderTime" id="reminder15min" value="15" class="form-radio text-primary-600"> | |
| <label for="reminder15min" class="text-gray-700">15 minutos antes</label> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="reminderTime" id="reminder30min" value="30" class="form-radio text-primary-600"> | |
| <label for="reminder30min" class="text-gray-700">30 minutos antes</label> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="reminderTime" id="reminder1h" value="60" class="form-radio text-primary-600"> | |
| <label for="reminder1h" class="text-gray-700">1 hora antes</label> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="reminderTime" id="reminder1d" value="1440" class="form-radio text-primary-600"> | |
| <label for="reminder1d" class="text-gray-700">1 dia antes</label> | |
| </div> | |
| </div> | |
| </div> | |
| <button type="submit" id="submitBtn" | |
| class="w-full bg-primary-600 text-white py-3 px-4 rounded-lg hover:bg-primary-700 transition-colors flex items-center justify-center"> | |
| <i class="fas fa-save mr-2"></i>Salvar Evento | |
| </button> | |
| </form> | |
| </div> | |
| <!-- Main content - Agenda --> | |
| <div class="w-full lg:w-2/3"> | |
| <!-- Month selector and filters --> | |
| <div class="bg-white rounded-lg shadow-md p-4 mb-6"> | |
| <div class="flex flex-col md:flex-row md:justify-between md:items-center gap-4"> | |
| <div class="flex items-center space-x-4"> | |
| <button id="prevMonth" class="p-2 rounded-full hover:bg-gray-100 transition-colors tooltip"> | |
| <i class="fas fa-chevron-left"></i> | |
| <span class="tooltip-text">Mês anterior</span> | |
| </button> | |
| <h2 id="currentMonth" class="text-xl font-semibold text-primary-700">Junho 2023</h2> | |
| <button id="nextMonth" class="p-2 rounded-full hover:bg-gray-100 transition-colors tooltip"> | |
| <i class="fas fa-chevron-right"></i> | |
| <span class="tooltip-text">Próximo mês</span> | |
| </button> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <button id="todayBtn" class="px-3 py-1 text-sm bg-primary-100 text-primary-700 rounded-lg hover:bg-primary-200 transition-colors"> | |
| Hoje | |
| </button> | |
| <div class="relative"> | |
| <button id="filterBtn" class="px-3 py-1 text-sm bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors flex items-center"> | |
| <i class="fas fa-filter mr-2"></i>Filtrar | |
| </button> | |
| <div id="filterDropdown" class="hidden absolute right-0 mt-1 w-48 bg-white rounded-md shadow-lg py-1 z-10 border border-gray-200"> | |
| <div class="px-3 py-2"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" id="filterAppointments" checked class="custom-checkbox"> | |
| <span class="text-gray-700">Compromissos</span> | |
| </label> | |
| </div> | |
| <div class="px-3 py-2"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" id="filterBlocks" checked class="custom-checkbox"> | |
| <span class="text-gray-700">Datas bloqueadas</span> | |
| </label> | |
| </div> | |
| <div class="border-t border-gray-200 my-1"></div> | |
| <div class="px-3 py-2"> | |
| <label class="block text-gray-700 text-sm mb-1">Empresas</label> | |
| <select id="companyFilter" class="w-full px-2 py-1 text-sm border border-gray-300 rounded"> | |
| <option value="">Todas</option> | |
| <!-- Options will be populated by JS --> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="relative"> | |
| <button id="viewBtn" class="px-3 py-1 text-sm bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors flex items-center"> | |
| <i class="fas fa-calendar-alt mr-2"></i>Mês | |
| </button> | |
| <div id="viewDropdown" class="hidden absolute right-0 mt-1 w-32 bg-white rounded-md shadow-lg py-1 z-10 border border-gray-200"> | |
| <a href="#" class="block px-3 py-1 text-sm text-gray-700 hover:bg-gray-100">Dia</a> | |
| <a href="#" class="block px-3 py-1 text-sm text-gray-700 hover:bg-gray-100">Semana</a> | |
| <a href="#" class="block px-3 py-1 text-sm text-gray-700 hover:bg-gray-100">Mês</a> | |
| <a href="#" class="block px-3 py-1 text-sm text-gray-700 hover:bg-gray-100">Ano</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Agenda list --> | |
| <div class="bg-white rounded-lg shadow-md overflow-hidden"> | |
| <div id="agendaList" class="divide-y divide-gray-200 max-h-[600px] overflow-y-auto"> | |
| <!-- Events will be loaded here --> | |
| <div class="p-6 text-center text-gray-500"> | |
| <div class="inline-block p-4"> | |
| <i class="fas fa-spinner fa-spin text-2xl text-primary-500 mb-2"></i> | |
| <p>Carregando agenda...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Empty state --> | |
| <div id="emptyState" class="hidden bg-white rounded-lg shadow-md p-8 text-center"> | |
| <div class="max-w-md mx-auto"> | |
| <div class="p-4 inline-block rounded-full bg-primary-100 text-primary-600 mb-4"> | |
| <i class="fas fa-calendar-plus text-3xl"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold text-gray-800 mb-2">Nenhum evento encontrado</h3> | |
| <p class="text-gray-600 mb-6">Adicione seu primeiro evento usando o formulário ao lado ou altere os filtros de busca.</p> | |
| <button id="addFirstEventBtn" class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"> | |
| <i class="fas fa-plus mr-2"></i>Adicionar Evento | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Edit Event Modal --> | |
| <div id="editModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50 px-4"> | |
| <div class="bg-white rounded-lg shadow-xl w-full max-w-md"> | |
| <div class="flex justify-between items-center border-b px-6 py-4"> | |
| <h3 class="text-lg font-semibold text-primary-700">Editar Evento</h3> | |
| <button id="closeEditModal" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-6"> | |
| <form id="editForm" class="space-y-4"> | |
| <input type="hidden" id="editEventId"> | |
| <div> | |
| <label class="block text-gray-700 mb-2 font-medium">Tipo de Evento</label> | |
| <div class="flex space-x-4"> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="editEventType" value="appointment" | |
| class="form-radio text-primary-600 focus:ring-primary-500"> | |
| <span class="ml-2">Compromisso</span> | |
| </label> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="editEventType" value="block" | |
| class="form-radio text-primary-600 focus:ring-primary-500"> | |
| <span class="ml-2">Bloquear Data</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="editEventDate" class="block text-gray-700 mb-2 font-medium">Data</label> | |
| <input type="date" id="editEventDate" required | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| </div> | |
| <div id="editCompanySection"> | |
| <label for="editCompanySelect" class="block text-gray-700 mb-2 font-medium">Empresa</label> | |
| <select id="editCompanySelect" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| <!-- Options will be populated by JS --> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="editEventDescription" class="block text-gray-700 mb-2 font-medium">Descrição</label> | |
| <textarea id="editEventDescription" rows="3" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"></textarea> | |
| </div> | |
| <div id="editFeeSection"> | |
| <label for="editEventFee" class="block text-gray-700 mb-2 font-medium">Valor do Cache (R$)</label> | |
| <div class="relative"> | |
| <span class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">R$</span> | |
| <input type="number" id="editEventFee" min="0" step="0.01" | |
| class="w-full pl-8 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"> | |
| </div> | |
| </div> | |
| <div id="editReminderSection" class="hidden"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" id="editSetReminder" class="custom-checkbox"> | |
| <span class="text-gray-700">Definir lembrete</span> | |
| </label> | |
| <div id="editReminderOptions" class="hidden mt-2 pl-6 space-y-2"> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="editReminderTime" id="editReminder15min" value="15" class="form-radio text-primary-600"> | |
| <label for="editReminder15min" class="text-gray-700">15 minutos antes</label> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="editReminderTime" id="editReminder30min" value="30" class="form-radio text-primary-600"> | |
| <label for="editReminder30min" class="text-gray-700">30 minutos antes</label> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="editReminderTime" id="editReminder1h" value="60" class="form-radio text-primary-600"> | |
| <label for="editReminder1h" class="text-gray-700">1 hora antes</label> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <input type="radio" name="editReminderTime" id="editReminder1d" value="1440" class="form-radio text-primary-600"> | |
| <label for="editReminder1d" class="text-gray-700">1 dia antes</label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex justify-end space-x-3 pt-4"> | |
| <button type="button" id="deleteEventBtn" | |
| class="px-4 py-2 bg-red-100 text-red-600 rounded-lg hover:bg-red-200 transition-colors flex items-center"> | |
| <i class="fas fa-trash mr-2"></i>Excluir | |
| </button> | |
| <button type="submit" | |
| class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors flex items-center"> | |
| <i class="fas fa-save mr-2"></i>Salvar | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Notification Toast --> | |
| <div id="toast" class="fixed bottom-4 right-4 hidden"> | |
| <div class="bg-gray-800 text-white px-4 py-3 rounded-lg shadow-lg flex items-start max-w-xs"> | |
| <div id="toastIcon" class="mr-3 mt-0.5"> | |
| <i class="fas fa-check-circle text-green-400"></i> | |
| </div> | |
| <div> | |
| <h4 id="toastTitle" class="font-semibold">Sucesso</h4> | |
| <p id="toastMessage" class="text-sm text-gray-300">Evento adicionado com sucesso</p> | |
| </div> | |
| <button id="closeToast" class="ml-4 text-gray-400 hover:text-white"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <script> | |
| // Sample data - in a real app, this would come from an API or database | |
| let companies = [ | |
| { id: 1, name: "Empresa A", color: "#0ea5e9" }, | |
| { id: 2, name: "Empresa B", color: "#10b981" }, | |
| { id: 3, name: "Empresa C", color: "#f59e0b" }, | |
| { id: 4, name: "Empresa D", color: "#8b5cf6" }, | |
| { id: 5, name: "Empresa E", color: "#ec4899" } | |
| ]; | |
| let events = [ | |
| { | |
| id: 1, | |
| date: getFormattedDate(new Date()), | |
| type: "appointment", | |
| companyId: 1, | |
| description: "Reunião de planejamento estratégico", | |
| fee: 2500.00, | |
| reminder: null | |
| }, | |
| { | |
| id: 2, | |
| date: getFormattedDate(addDays(new Date(), 2)), | |
| type: "appointment", | |
| companyId: 2, | |
| description: "Apresentação de resultados trimestrais", | |
| fee: 1800.00, | |
| reminder: 30 | |
| }, | |
| { | |
| id: 3, | |
| date: getFormattedDate(addDays(new Date(), 5)), | |
| type: "block", | |
| companyId: null, | |
| description: "Férias - Viagem familiar", | |
| fee: 0, | |
| reminder: null | |
| }, | |
| { | |
| id: 4, | |
| date: getFormattedDate(addDays(new Date(), 7)), | |
| type: "appointment", | |
| companyId: 3, | |
| description: "Workshop de treinamento", | |
| fee: 3200.00, | |
| reminder: 1440 | |
| }, | |
| { | |
| id: 5, | |
| date: getFormattedDate(addDays(new Date(), 10)), | |
| type: "appointment", | |
| companyId: 4, | |
| description: "Negociação de contrato", | |
| fee: 1500.00, | |
| reminder: 60 | |
| } | |
| ]; | |
| // Current month for the agenda view | |
| let currentDate = new Date(); | |
| let showAppointments = true; | |
| let showBlocks = true; | |
| let companyFilter = null; | |
| // DOM elements | |
| const eventForm = document.getElementById('eventForm'); | |
| const eventTypeRadios = document.querySelectorAll('input[name="eventType"]'); | |
| const companySection = document.getElementById('companySection'); | |
| const newCompanySection = document.getElementById('newCompanySection'); | |
| const descriptionSection = document.getElementById('descriptionSection'); | |
| const feeSection = document.getElementById('feeSection'); | |
| const reminderSection = document.getElementById('reminderSection'); | |
| const setReminder = document.getElementById('setReminder'); | |
| const reminderOptions = document.getElementById('reminderOptions'); | |
| const companySelect = document.getElementById('companySelect'); | |
| const addCompanyBtn = document.getElementById('addCompanyBtn'); | |
| const cancelAddCompany = document.getElementById('cancelAddCompany'); | |
| const newCompanyName = document.getElementById('newCompanyName'); | |
| const companyColor = document.getElementById('companyColor'); | |
| const eventDate = document.getElementById('eventDate'); | |
| const eventDescription = document.getElementById('eventDescription'); | |
| const eventFee = document.getElementById('eventFee'); | |
| const clearFormBtn = document.getElementById('clearFormBtn'); | |
| const agendaList = document.getElementById('agendaList'); | |
| const emptyState = document.getElementById('emptyState'); | |
| const addFirstEventBtn = document.getElementById('addFirstEventBtn'); | |
| const currentMonth = document.getElementById('currentMonth'); | |
| const prevMonth = document.getElementById('prevMonth'); | |
| const nextMonth = document.getElementById('nextMonth'); | |
| const todayBtn = document.getElementById('todayBtn'); | |
| const filterBtn = document.getElementById('filterBtn'); | |
| const filterDropdown = document.getElementById('filterDropdown'); | |
| const filterAppointments = document.getElementById('filterAppointments'); | |
| const filterBlocks = document.getElementById('filterBlocks'); | |
| const companyFilterSelect = document.getElementById('companyFilter'); | |
| const viewBtn = document.getElementById('viewBtn'); | |
| const viewDropdown = document.getElementById('viewDropdown'); | |
| const editModal = document.getElementById('editModal'); | |
| const closeEditModal = document.getElementById('closeEditModal'); | |
| const editForm = document.getElementById('editForm'); | |
| const editEventId = document.getElementById('editEventId'); | |
| const editEventTypeRadios = document.querySelectorAll('input[name="editEventType"]'); | |
| const editCompanySection = document.getElementById('editCompanySection'); | |
| const editFeeSection = document.getElementById('editFeeSection'); | |
| const editReminderSection = document.getElementById('editReminderSection'); | |
| const editSetReminder = document.getElementById('editSetReminder'); | |
| const editReminderOptions = document.getElementById('editReminderOptions'); | |
| const editEventDate = document.getElementById('editEventDate'); | |
| const editCompanySelect = document.getElementById('editCompanySelect'); | |
| const editEventDescription = document.getElementById('editEventDescription'); | |
| const editEventFee = document.getElementById('editEventFee'); | |
| const deleteEventBtn = document.getElementById('deleteEventBtn'); | |
| const appointmentsCount = document.getElementById('appointmentsCount'); | |
| const blocksCount = document.getElementById('blocksCount'); | |
| const totalFee = document.getElementById('totalFee'); | |
| const todayDate = document.getElementById('todayDate'); | |
| const syncBtn = document.getElementById('syncBtn'); | |
| const userMenuBtn = document.getElementById('userMenuBtn'); | |
| const userMenu = document.getElementById('userMenu'); | |
| const toast = document.getElementById('toast'); | |
| const toastTitle = document.getElementById('toastTitle'); | |
| const toastMessage = document.getElementById('toastMessage'); | |
| const toastIcon = document.getElementById('toastIcon'); | |
| const closeToast = document.getElementById('closeToast'); | |
| // Initialize the app | |
| function init() { | |
| // Set today's date as default | |
| const today = new Date(); | |
| eventDate.value = getFormattedDate(today); | |
| // Display today's date | |
| const options = { weekday: 'long', day: 'numeric', month: 'long' }; | |
| todayDate.textContent = today.toLocaleDateString('pt-BR', options); | |
| // Populate company dropdowns | |
| populateCompanyDropdowns(); | |
| // Load events for current month | |
| loadAgenda(); | |
| // Update statistics | |
| updateStatistics(); | |
| // Set up event listeners | |
| setupEventListeners(); | |
| } | |
| // Set up all event listeners | |
| function setupEventListeners() { | |
| // Event type radio buttons | |
| eventTypeRadios.forEach(radio => { | |
| radio.addEventListener('change', toggleEventFields); | |
| }); | |
| // Add company button | |
| addCompanyBtn.addEventListener('click', () => { | |
| companySection.classList.add('hidden'); | |
| newCompanySection.classList.remove('hidden'); | |
| }); | |
| // Cancel adding new company | |
| cancelAddCompany.addEventListener('click', () => { | |
| companySection.classList.remove('hidden'); | |
| newCompanySection.classList.add('hidden'); | |
| newCompanyName.value = ''; | |
| }); | |
| // Clear form button | |
| clearFormBtn.addEventListener('click', resetForm); | |
| // Form submission | |
| eventForm.addEventListener('submit', addNewEvent); | |
| // Reminder checkbox | |
| setReminder.addEventListener('change', () => { | |
| reminderOptions.classList.toggle('hidden', !setReminder.checked); | |
| }); | |
| // Month navigation | |
| prevMonth.addEventListener('click', () => { | |
| currentDate.setMonth(currentDate.getMonth() - 1); | |
| loadAgenda(); | |
| }); | |
| nextMonth.addEventListener('click', () => { | |
| currentDate.setMonth(currentDate.getMonth() + 1); | |
| loadAgenda(); | |
| }); | |
| // Today button | |
| todayBtn.addEventListener('click', () => { | |
| currentDate = new Date(); | |
| loadAgenda(); | |
| }); | |
| // Filter button | |
| filterBtn.addEventListener('click', () => { | |
| filterDropdown.classList.toggle('hidden'); | |
| viewDropdown.classList.add('hidden'); | |
| }); | |
| // View button | |
| viewBtn.addEventListener('click', () => { | |
| viewDropdown.classList.toggle('hidden'); | |
| filterDropdown.classList.add('hidden'); | |
| }); | |
| // Filter checkboxes | |
| filterAppointments.addEventListener('change', () => { | |
| showAppointments = filterAppointments.checked; | |
| loadAgenda(); | |
| }); | |
| filterBlocks.addEventListener('change', () => { | |
| showBlocks = filterBlocks.checked; | |
| loadAgenda(); | |
| }); | |
| // Company filter | |
| companyFilterSelect.addEventListener('change', () => { | |
| companyFilter = companyFilterSelect.value ? parseInt(companyFilterSelect.value) : null; | |
| loadAgenda(); | |
| }); | |
| // Edit modal | |
| closeEditModal.addEventListener('click', () => { | |
| editModal.classList.add('hidden'); | |
| }); | |
| // Edit form submission | |
| editForm.addEventListener('submit', saveEditedEvent); | |
| // Edit reminder checkbox | |
| editSetReminder.addEventListener('change', () => { | |
| editReminderOptions.classList.toggle('hidden', !editSetReminder.checked); | |
| }); | |
| // Delete event button | |
| deleteEventBtn.addEventListener('click', deleteEvent); | |
| // Sync button | |
| syncBtn.addEventListener('click', syncData); | |
| // User menu | |
| userMenuBtn.addEventListener('click', () => { | |
| userMenu.classList.toggle('hidden'); | |
| }); | |
| // Close toast | |
| closeToast.addEventListener('click', () => { | |
| toast.classList.add('hidden'); | |
| }); | |
| // Close dropdowns when clicking outside | |
| document.addEventListener('click', (e) => { | |
| if (!filterBtn.contains(e.target)) { | |
| filterDropdown.classList.add('hidden'); | |
| } | |
| if (!viewBtn.contains(e.target)) { | |
| viewDropdown.classList.add('hidden'); | |
| } | |
| if (!userMenuBtn.contains(e.target)) { | |
| userMenu.classList.add('hidden'); | |
| } | |
| }); | |
| // Add first event button | |
| addFirstEventBtn.addEventListener('click', () => { | |
| // Scroll to form | |
| document.querySelector('.lg\\:w-1\\/3').scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| } | |
| // Helper functions | |
| function getFormattedDate(date) { | |
| const year = date.getFullYear(); | |
| const month = String(date.getMonth() + 1).padStart(2, '0'); | |
| const day = String(date.getDate()).padStart(2, '0'); | |
| return `${year}-${month}-${day}`; | |
| } | |
| function addDays(date, days) { | |
| const result = new Date(date); | |
| result.setDate(result.getDate() + days); | |
| return result; | |
| } | |
| function formatDateForDisplay(dateString) { | |
| const [year, month, day] = dateString.split('-'); | |
| return `${day}/${month}/${year}`; | |
| } | |
| function formatCurrency(value) { | |
| return new Intl.NumberFormat('pt-BR', { | |
| style: 'currency', | |
| currency: 'BRL' | |
| }).format(value); | |
| } | |
| function showNotification(title, message, type = 'success') { | |
| // Set icon based on type | |
| let iconClass; | |
| switch(type) { | |
| case 'success': | |
| iconClass = 'fas fa-check-circle text-green-400'; | |
| break; | |
| case 'error': | |
| iconClass = 'fas fa-exclamation-circle text-red-400'; | |
| break; | |
| case 'warning': | |
| iconClass = 'fas fa-exclamation-triangle text-amber-400'; | |
| break; | |
| case 'info': | |
| iconClass = 'fas fa-info-circle text-blue-400'; | |
| break; | |
| default: | |
| iconClass = 'fas fa-check-circle text-green-400'; | |
| } | |
| toastIcon.innerHTML = `<i class="${iconClass}"></i>`; | |
| toastTitle.textContent = title; | |
| toastMessage.textContent = message; | |
| toast.classList.remove('hidden'); | |
| // Auto-hide after 5 seconds | |
| setTimeout(() => { | |
| toast.classList.add('hidden'); | |
| }, 5000); | |
| } | |
| // Toggle form fields based on event type | |
| function toggleEventFields() { | |
| const selectedType = document.querySelector('input[name="eventType"]:checked').value; | |
| if (selectedType === 'block') { | |
| companySection.classList.add('hidden'); | |
| descriptionSection.classList.remove('hidden'); | |
| feeSection.classList.add('hidden'); | |
| reminderSection.classList.add('hidden'); | |
| } else { | |
| companySection.classList.remove('hidden'); | |
| descriptionSection.classList.remove('hidden'); | |
| feeSection.classList.remove('hidden'); | |
| reminderSection.classList.remove('hidden'); | |
| } | |
| } | |
| // Reset form to default state | |
| function resetForm() { | |
| eventForm.reset(); | |
| eventDate.value = getFormattedDate(new Date()); | |
| companySection.classList.remove('hidden'); | |
| newCompanySection.classList.add('hidden'); | |
| newCompanyName.value = ''; | |
| reminderOptions.classList.add('hidden'); | |
| showNotification('Formulário limpo', 'O formulário foi resetado para o estado inicial', 'info'); | |
| } | |
| // Populate company dropdowns | |
| function populateCompanyDropdowns() { | |
| // Clear existing options | |
| companySelect.innerHTML = '<option value="">Selecione uma empresa</option>'; | |
| editCompanySelect.innerHTML = '<option value="">Selecione uma empresa</option>'; | |
| companyFilterSelect.innerHTML = '<option value="">Todas</option>'; | |
| // Add companies | |
| companies.forEach(company => { | |
| const option = document.createElement('option'); | |
| option.value = company.id; | |
| option.textContent = company.name; | |
| companySelect.appendChild(option.cloneNode(true)); | |
| editCompanySelect.appendChild(option.cloneNode(true)); | |
| // Add to filter dropdown | |
| const filterOption = option.cloneNode(true); | |
| companyFilterSelect.appendChild(filterOption); | |
| }); | |
| } | |
| // Add new event | |
| function addNewEvent(e) { | |
| e.preventDefault(); | |
| const submitBtn = document.getElementById('submitBtn'); | |
| const originalBtnContent = submitBtn.innerHTML; | |
| // Show loading state | |
| submitBtn.disabled = true; | |
| submitBtn.innerHTML = '<div class="spinner mr-2"></div> Salvando...'; | |
| const eventType = document.querySelector('input[name="eventType"]:checked').value; | |
| const date = eventDate.value; | |
| let companyId = null; | |
| let description = eventDescription.value; | |
| let fee = 0; | |
| let reminder = null; | |
| if (eventType === 'appointment') { | |
| // Check if adding a new company | |
| if (!newCompanySection.classList.contains('hidden')) { | |
| const companyName = newCompanyName.value.trim(); | |
| const color = companyColor.value; | |
| if (!companyName) { | |
| showNotification('Erro', 'Por favor, insira um nome para a empresa', 'error'); | |
| submitBtn.disabled = false; | |
| submitBtn.innerHTML = originalBtnContent; | |
| return; | |
| } | |
| // Add new company (in a real app, this would be an API call) | |
| const newCompany = { | |
| id: companies.length > 0 ? Math.max(...companies.map(c => c.id)) + 1 : 1, | |
| name: companyName, | |
| color: color | |
| }; | |
| companies.push(newCompany); | |
| companyId = newCompany.id; | |
| // Reset new company form | |
| companySection.classList.remove('hidden'); | |
| newCompanySection.classList.add('hidden'); | |
| newCompanyName.value = ''; | |
| // Update dropdowns | |
| populateCompanyDropdowns(); | |
| } else { | |
| companyId = parseInt(companySelect.value); | |
| if (!companyId) { | |
| showNotification('Erro', 'Por favor, selecione uma empresa', 'error'); | |
| submitBtn.disabled = false; | |
| submitBtn.innerHTML = originalBtnContent; | |
| return; | |
| } | |
| } | |
| fee = parseFloat(eventFee.value) || 0; | |
| // Set reminder if enabled | |
| if (setReminder.checked) { | |
| const reminderTime = document.querySelector('input[name="reminderTime"]:checked'); | |
| if (reminderTime) { | |
| reminder = parseInt(reminderTime.value); | |
| } | |
| } | |
| } else { | |
| description = description || 'Data bloqueada'; | |
| } | |
| // Add new event (in a real app, this would be an API call) | |
| const newEvent = { | |
| id: events.length > 0 ? Math.max(...events.map(e => e.id)) + 1 : 1, | |
| date: date, | |
| type: eventType, | |
| companyId: companyId, | |
| description: description, | |
| fee: fee, | |
| reminder: reminder | |
| }; | |
| events.push(newEvent); | |
| // Reset form | |
| eventDescription.value = ''; | |
| eventFee.value = ''; | |
| setReminder.checked = false; | |
| reminderOptions.classList.add('hidden'); | |
| // Reload agenda | |
| loadAgenda(); | |
| updateStatistics(); | |
| // Show success message | |
| showNotification('Sucesso', 'Evento adicionado com sucesso!'); | |
| // Restore button state | |
| submitBtn.disabled = false; | |
| submitBtn.innerHTML = originalBtnContent; | |
| } | |
| // Load agenda for current month with filters | |
| function loadAgenda() { | |
| // Update month display | |
| const monthNames = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", | |
| "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"]; | |
| currentMonth.textContent = `${monthNames[currentDate.getMonth()]} ${currentDate.getFullYear()}`; | |
| // Get first and last day of current month | |
| const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); | |
| const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); | |
| // Filter events for current month with applied filters | |
| let monthEvents = events.filter(event => { | |
| const eventDate = new Date(event.date); | |
| const dateInRange = eventDate >= firstDay && eventDate <= lastDay; | |
| const typeMatches = (event.type === 'appointment' && showAppointments) || | |
| (event.type === 'block' && showBlocks); | |
| const companyMatches = !companyFilter || event.companyId === companyFilter; | |
| return dateInRange && typeMatches && companyMatches; | |
| }).sort((a, b) => new Date(a.date) - new Date(b.date)); | |
| // Display events or empty state | |
| if (monthEvents.length === 0) { | |
| agendaList.classList.add('hidden'); | |
| emptyState.classList.remove('hidden'); | |
| return; | |
| } | |
| agendaList.classList.remove('hidden'); | |
| emptyState.classList.add('hidden'); | |
| let agendaHTML = ''; | |
| let currentDay = null; | |
| monthEvents.forEach((event, index) => { | |
| const eventDate = new Date(event.date); | |
| const displayDate = formatDateForDisplay(event.date); | |
| // Add day header if it's a new day | |
| if (!currentDay || currentDay.getDate() !== eventDate.getDate()) { | |
| currentDay = eventDate; | |
| const dayName = currentDay.toLocaleDateString('pt-BR', { weekday: 'long' }); | |
| const isToday = currentDay.toDateString() === new Date().toDateString(); | |
| agendaHTML += ` | |
| <div class="bg-gray-50 px-4 py-2 border-b border-gray-200 ${isToday ? 'bg-primary-50' : ''}"> | |
| <h3 class="font-medium text-gray-700 ${isToday ? 'text-primary-700' : ''}"> | |
| ${dayName.charAt(0).toUpperCase() + dayName.slice(1)}, ${displayDate} | |
| ${isToday ? '<span class="ml-2 text-xs bg-primary-600 text-white px-2 py-0.5 rounded-full">Hoje</span>' : ''} | |
| </h3> | |
| </div> | |
| `; | |
| } | |
| // Get company info if it's an appointment | |
| let company = null; | |
| let color = '#94a3b8'; // Default gray for blocked dates | |
| let typeIcon = 'fas fa-calendar-day'; | |
| let typeClass = 'text-primary-600'; | |
| if (event.type === 'appointment' && event.companyId) { | |
| company = companies.find(c => c.id === event.companyId); | |
| if (company) color = company.color; | |
| typeIcon = 'fas fa-handshake'; | |
| typeClass = 'text-primary-600'; | |
| } else { | |
| typeIcon = 'fas fa-ban'; | |
| typeClass = 'text-red-600'; | |
| } | |
| // Create event card | |
| agendaHTML += ` | |
| <div class="fade-in px-4 py-3 hover:bg-gray-50 ${event.type === 'block' ? 'bg-red-50 hover:bg-red-100' : ''}" data-event-id="${event.id}"> | |
| <div class="flex items-start"> | |
| <div class="w-3 h-3 rounded-full mt-1.5 mr-3 flex-shrink-0" style="background-color: ${color};"></div> | |
| <div class="flex-grow"> | |
| <div class="flex justify-between items-start"> | |
| <div class="flex items-center"> | |
| <i class="${typeIcon} ${typeClass} mr-2 text-sm"></i> | |
| <h4 class="font-medium ${event.type === 'block' ? 'text-red-700' : 'text-primary-700'}"> | |
| ${event.type === 'block' ? 'Data Bloqueada' : (company ? company.name : 'Empresa desconhecida')} | |
| </h4> | |
| </div> | |
| <div class="flex space-x-2"> | |
| ${event.reminder ? ` | |
| <span class="text-xs bg-gray-200 text-gray-700 px-2 py-0.5 rounded-full flex items-center"> | |
| <i class="fas fa-bell text-xs mr-1 text-amber-500"></i> | |
| ${getReminderText(event.reminder)} | |
| </span> | |
| ` : ''} | |
| <button class="edit-event text-gray-400 hover:text-primary-600 transition-colors tooltip" data-event-id="${event.id}"> | |
| <i class="fas fa-edit"></i> | |
| <span class="tooltip-text">Editar</span> | |
| </button> | |
| <button class="delete-event text-gray-400 hover:text-red-600 transition-colors tooltip" data-event-id="${event.id}"> | |
| <i class="fas fa-trash"></i> | |
| <span class="tooltip-text">Excluir</span> | |
| </button> | |
| </div> | |
| </div> | |
| <p class="text-sm text-gray-600 mt-1">${event.description}</p> | |
| ${event.type === 'appointment' ? ` | |
| <p class="text-sm font-medium text-green-600 mt-1"> | |
| ${formatCurrency(event.fee)} | |
| </p> | |
| ` : ''} | |
| <p class="text-xs text-gray-400 mt-2"> | |
| <i class="fas fa-clock mr-1"></i> | |
| Criado em ${new Date().toLocaleDateString('pt-BR')} | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| agendaList.innerHTML = agendaHTML; | |
| // Add event listeners to edit and delete buttons | |
| document.querySelectorAll('.edit-event').forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const eventId = parseInt(button.dataset.eventId); | |
| openEditModal(eventId); | |
| }); | |
| }); | |
| document.querySelectorAll('.delete-event').forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const eventId = parseInt(button.dataset.eventId); | |
| if (confirm('Tem certeza que deseja excluir este evento?')) { | |
| deleteEvent(eventId); | |
| } | |
| }); | |
| }); | |
| } | |
| // Helper function to get reminder text | |
| function getReminderText(minutes) { | |
| if (minutes === 15) return '15 min antes'; | |
| if (minutes === 30) return '30 min antes'; | |
| if (minutes === 60) return '1 hora antes'; | |
| if (minutes === 1440) return '1 dia antes'; | |
| return 'Lembrete'; | |
| } | |
| // Open edit modal with event data | |
| function openEditModal(eventId) { | |
| const event = events.find(e => e.id === eventId); | |
| if (!event) return; | |
| // Set form values | |
| editEventId.value = event.id; | |
| // Set event type | |
| editEventTypeRadios.forEach(radio => { | |
| radio.checked = radio.value === event.type; | |
| }); | |
| editEventDate.value = event.date; | |
| editCompanySelect.value = event.companyId || ''; | |
| editEventDescription.value = event.description; | |
| editEventFee.value = event.fee; | |
| // Set reminder if exists | |
| if (event.reminder) { | |
| editSetReminder.checked = true; | |
| editReminderOptions.classList.remove('hidden'); | |
| document.querySelector(`input[name="editReminderTime"][value="${event.reminder}"]`).checked = true; | |
| } else { | |
| editSetReminder.checked = false; | |
| editReminderOptions.classList.add('hidden'); | |
| } | |
| // Toggle fields based on event type | |
| if (event.type === 'block') { | |
| editCompanySection.classList.add('hidden'); | |
| editFeeSection.classList.add('hidden'); | |
| editReminderSection.classList.add('hidden'); | |
| } else { | |
| editCompanySection.classList.remove('hidden'); | |
| editFeeSection.classList.remove('hidden'); | |
| editReminderSection.classList.remove('hidden'); | |
| } | |
| // Show modal | |
| editModal.classList.remove('hidden'); | |
| } | |
| // Save edited event | |
| function saveEditedEvent(e) { | |
| e.preventDefault(); | |
| const eventId = parseInt(editEventId.value); | |
| const eventIndex = events.findIndex(e => e.id === eventId); | |
| if (eventIndex === -1) return; | |
| const eventType = document.querySelector('input[name="editEventType"]:checked').value; | |
| const date = editEventDate.value; | |
| const companyId = eventType === 'appointment' ? parseInt(editCompanySelect.value) : null; | |
| const description = editEventDescription.value; | |
| const fee = eventType === 'appointment' ? parseFloat(editEventFee.value) || 0 : 0; | |
| let reminder = null; | |
| if (eventType === 'appointment' && editSetReminder.checked) { | |
| const reminderTime = document.querySelector('input[name="editReminderTime"]:checked'); | |
| if (reminderTime) { | |
| reminder = parseInt(reminderTime.value); | |
| } | |
| } | |
| // Update event (in a real app, this would be an API call) | |
| events[eventIndex] = { | |
| ...events[eventIndex], | |
| date: date, | |
| type: eventType, | |
| companyId: companyId, | |
| description: description, | |
| fee: fee, | |
| reminder: reminder | |
| }; | |
| // Close modal | |
| editModal.classList.add('hidden'); | |
| // Reload agenda | |
| loadAgenda(); | |
| updateStatistics(); | |
| // Show success message | |
| showNotification('Sucesso', 'Evento atualizado com sucesso!'); | |
| } | |
| // Delete event | |
| function deleteEvent(eventId = null) { | |
| if (!eventId) { | |
| eventId = parseInt(editEventId.value); | |
| } | |
| // Delete event (in a real app, this would be an API call) | |
| events = events.filter(e => e.id !== eventId); | |
| // Close modal if open | |
| editModal.classList.add('hidden'); | |
| // Reload agenda | |
| loadAgenda(); | |
| updateStatistics(); | |
| // Show success message | |
| showNotification('Sucesso', 'Evento excluído com sucesso!'); | |
| } | |
| // Update statistics | |
| function updateStatistics() { | |
| const appointments = events.filter(e => e.type === 'appointment').length; | |
| const blocks = events.filter(e => e.type === 'block').length; | |
| const total = events.reduce((sum, event) => sum + (event.fee || 0), 0); | |
| appointmentsCount.textContent = appointments; | |
| blocksCount.textContent = blocks; | |
| totalFee.textContent = formatCurrency(total); | |
| } | |
| // Sync data (simulated) | |
| function syncData() { | |
| const originalBtnContent = syncBtn.innerHTML; | |
| // Show loading state | |
| syncBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Sincronizando'; | |
| syncBtn.disabled = true; | |
| // Simulate API call | |
| setTimeout(() => { | |
| // In a real app, this would fetch data from server | |
| showNotification('Sincronizado', 'Dados sincronizados com sucesso!'); | |
| // Restore button state | |
| syncBtn.innerHTML = originalBtnContent; | |
| syncBtn.disabled = false; | |
| }, 1500); | |
| } | |
| // Initialize the app when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', init); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sidneibarreto/projetos" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |