setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Criar tabelas se não existirem
$pdo->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
phone VARCHAR(20),
avatar VARCHAR(255),
balance DECIMAL(10,2) DEFAULT 0.00,
nikapay_token VARCHAR(255),
nikapay_secret VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS funnels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
slug VARCHAR(255) NOT NULL,
logo_url VARCHAR(255),
primary_color VARCHAR(7) DEFAULT '#8B5CF6',
secondary_color VARCHAR(7) DEFAULT '#0EA5E9',
active INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS funnel_steps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
funnel_id INTEGER NOT NULL,
type VARCHAR(50) NOT NULL,
question_text TEXT NOT NULL,
options TEXT,
validation VARCHAR(255),
next_step INTEGER,
order_index INTEGER NOT NULL,
required INTEGER DEFAULT 1,
FOREIGN KEY (funnel_id) REFERENCES funnels(id)
);
CREATE TABLE IF NOT EXISTS clients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
email VARCHAR(255),
phone VARCHAR(20),
document VARCHAR(20),
visits INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS appointments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
client_id INTEGER NOT NULL,
funnel_id INTEGER,
service VARCHAR(255) NOT NULL,
appointment_date DATE NOT NULL,
appointment_time TIME NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
payment_id VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (funnel_id) REFERENCES funnels(id)
);
CREATE TABLE IF NOT EXISTS payments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
appointment_id INTEGER,
amount DECIMAL(10,2) NOT NULL,
type VARCHAR(20) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
external_id VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (appointment_id) REFERENCES appointments(id)
);
CREATE TABLE IF NOT EXISTS withdrawals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
amount DECIMAL(10,2) NOT NULL,
pix_key VARCHAR(255) NOT NULL,
pix_key_type VARCHAR(20) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
external_id VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS funnel_responses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
funnel_id INTEGER NOT NULL,
client_id INTEGER,
step_id INTEGER NOT NULL,
response TEXT,
session_id VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (funnel_id) REFERENCES funnels(id),
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (step_id) REFERENCES funnel_steps(id)
);
");
// Inserir usuário demo se não existir
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE email = ?");
$stmt->execute(['demo@axionlinq.com']);
if ($stmt->fetchColumn() == 0) {
$stmt = $pdo->prepare("INSERT INTO users (name, email, password, phone, nikapay_token, nikapay_secret, balance) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
'Prestador Demo',
'demo@axionlinq.com',
password_hash('123456', PASSWORD_DEFAULT),
'(11) 99999-9999',
'ci__a567a44d-dca1-4b96-99e7-cb2c025722a2',
'cs__84371af8-8021-4490-861c-b54f6c0f01b2',
1250.75
]);
}
} catch (PDOException $e) {
die("Erro no banco: " . $e->getMessage());
}
// Funções auxiliares
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
function requireLogin() {
if (!isLoggedIn()) {
header('Location: ?action=login');
exit;
}
}
function getCurrentUser($pdo) {
if (!isLoggedIn()) return null;
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
function sanitize($input) {
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}
function formatCurrency($value) {
return 'R$ ' . number_format($value, 2, ',', '.');
}
// Processamento de ações
$action = $_GET['action'] ?? 'dashboard';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$postAction = $_POST['action'] ?? '';
switch ($postAction) {
case 'login':
$email = sanitize($_POST['email']);
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
header('Location: ?action=dashboard');
exit;
} else {
$error = 'Email ou senha inválidos';
}
break;
case 'register':
$name = sanitize($_POST['name']);
$email = sanitize($_POST['email']);
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
$phone = sanitize($_POST['phone']);
try {
$stmt = $pdo->prepare("INSERT INTO users (name, email, password, phone, nikapay_token, nikapay_secret) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $email, $password, $phone, 'ci__a567a44d-dca1-4b96-99e7-cb2c025722a2', 'cs__84371af8-8021-4490-861c-b54f6c0f01b2']);
session_regenerate_id(true);
$_SESSION['user_id'] = $pdo->lastInsertId();
$_SESSION['user_name'] = $name;
header('Location: ?action=dashboard');
exit;
} catch (PDOException $e) {
$error = 'Email já cadastrado';
}
break;
case 'logout':
session_destroy();
header('Location: ?action=login');
exit;
case 'create_funnel':
requireLogin();
$name = sanitize($_POST['name']);
$slug = sanitize(strtolower(str_replace(' ', '-', $_POST['slug'])));
$stmt = $pdo->prepare("INSERT INTO funnels (user_id, name, slug) VALUES (?, ?, ?)");
$stmt->execute([$_SESSION['user_id'], $name, $slug]);
echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
exit;
case 'save_appointment':
requireLogin();
$clientName = sanitize($_POST['client_name']);
$clientPhone = sanitize($_POST['client_phone']);
$clientEmail = sanitize($_POST['client_email']);
$service = sanitize($_POST['service']);
$date = $_POST['date'];
$time = $_POST['time'];
$amount = floatval($_POST['amount']);
// Inserir cliente se não existir
$stmt = $pdo->prepare("SELECT id FROM clients WHERE phone = ? AND user_id = ?");
$stmt->execute([$clientPhone, $_SESSION['user_id']]);
$client = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$client) {
$stmt = $pdo->prepare("INSERT INTO clients (user_id, name, phone, email, visits) VALUES (?, ?, ?, ?, 1)");
$stmt->execute([$_SESSION['user_id'], $clientName, $clientPhone, $clientEmail]);
$clientId = $pdo->lastInsertId();
} else {
$clientId = $client['id'];
$stmt = $pdo->prepare("UPDATE clients SET visits = visits + 1 WHERE id = ?");
$stmt->execute([$clientId]);
}
// Inserir agendamento
$stmt = $pdo->prepare("INSERT INTO appointments (user_id, client_id, service, appointment_date, appointment_time, amount, status) VALUES (?, ?, ?, ?, ?, ?, 'confirmed')");
$stmt->execute([$_SESSION['user_id'], $clientId, $service, $date, $time, $amount]);
// Atualizar saldo
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?");
$stmt->execute([$amount, $_SESSION['user_id']]);
echo json_encode(['success' => true]);
exit;
case 'request_withdrawal':
requireLogin();
$amount = floatval($_POST['amount']);
$pixKey = sanitize($_POST['pix_key']);
$pixKeyType = sanitize($_POST['pix_key_type']);
$user = getCurrentUser($pdo);
if ($user['balance'] >= $amount) {
$stmt = $pdo->prepare("INSERT INTO withdrawals (user_id, amount, pix_key, pix_key_type) VALUES (?, ?, ?, ?)");
$stmt->execute([$_SESSION['user_id'], $amount, $pixKey, $pixKeyType]);
$stmt = $pdo->prepare("UPDATE users SET balance = balance - ? WHERE id = ?");
$stmt->execute([$amount, $_SESSION['user_id']]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Saldo insuficiente']);
}
exit;
}
}
// AJAX endpoints
if (isset($_GET['ajax'])) {
header('Content-Type: application/json');
switch ($_GET['ajax']) {
case 'dashboard_data':
requireLogin();
// Estatísticas gerais
$stmt = $pdo->prepare("SELECT COUNT(*) FROM clients WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$totalClients = $stmt->fetchColumn();
$stmt = $pdo->prepare("SELECT COUNT(*) FROM appointments WHERE user_id = ? AND status = 'confirmed'");
$stmt->execute([$_SESSION['user_id']]);
$totalAppointments = $stmt->fetchColumn();
$stmt = $pdo->prepare("SELECT SUM(amount) FROM appointments WHERE user_id = ? AND status = 'confirmed' AND DATE(created_at) = DATE('now')");
$stmt->execute([$_SESSION['user_id']]);
$todayRevenue = $stmt->fetchColumn() ?: 0;
$stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$balance = $stmt->fetchColumn();
// Dados para gráficos (últimos 7 dias)
$revenueData = [];
for ($i = 6; $i >= 0; $i--) {
$date = date('Y-m-d', strtotime("-$i days"));
$stmt = $pdo->prepare("SELECT SUM(amount) FROM appointments WHERE user_id = ? AND status = 'confirmed' AND DATE(created_at) = ?");
$stmt->execute([$_SESSION['user_id'], $date]);
$revenueData[] = floatval($stmt->fetchColumn() ?: 0);
}
echo json_encode([
'totalClients' => $totalClients,
'totalAppointments' => $totalAppointments,
'todayRevenue' => $todayRevenue,
'balance' => $balance,
'revenueData' => $revenueData
]);
exit;
case 'clients_list':
requireLogin();
$stmt = $pdo->prepare("SELECT * FROM clients WHERE user_id = ? ORDER BY created_at DESC");
$stmt->execute([$_SESSION['user_id']]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
exit;
case 'appointments_list':
requireLogin();
$stmt = $pdo->prepare("
SELECT a.*, c.name as client_name, c.phone as client_phone
FROM appointments a
JOIN clients c ON a.client_id = c.id
WHERE a.user_id = ?
ORDER BY a.appointment_date DESC, a.appointment_time DESC
");
$stmt->execute([$_SESSION['user_id']]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
exit;
case 'funnels_list':
requireLogin();
$stmt = $pdo->prepare("SELECT * FROM funnels WHERE user_id = ? ORDER BY created_at DESC");
$stmt->execute([$_SESSION['user_id']]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
exit;
}
}
// Exibir funnel público
if (isset($_GET['f'])) {
$parts = explode('/', $_GET['f']);
$funnelId = intval($parts[0]);
$slug = $parts[1] ?? '';
$stmt = $pdo->prepare("SELECT * FROM funnels WHERE id = ? AND slug = ? AND active = 1");
$stmt->execute([$funnelId, $slug]);
$funnel = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$funnel) {
die("Funil não encontrado");
}
// Buscar steps do funil
$stmt = $pdo->prepare("SELECT * FROM funnel_steps WHERE funnel_id = ? ORDER BY order_index");
$stmt->execute([$funnelId]);
$steps = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
AxionLinQ - Sistema SaaS
Bem-vindo de volta
Criar conta
Receita dos Últimos 7 Dias
Lista de Clientes
Nome |
Telefone |
Email |
Visitas |
Cadastro |
Ações |
|
Cliente |
Serviço |
Data |
Horário |
Valor |
Status |
Ações |
|
WhatsApp Web
Envie mensagens automáticas para seus clientes
Histórico de Transações
Data |
Tipo |
Descrição |
Valor |
Status |
10/09/2025 |
Entrada |
Pagamento - Corte Masculino |
+ R$ 50,00 |
Confirmado |
09/09/2025 |
Saque |
Saque via Pix |
- R$ 200,00 |
Processado |
Página não encontrada';
break;
}
?>
prepare("UPDATE payments SET status = 'paid' WHERE external_id = ?");
$stmt->execute([$data['external_id']]);
// Atualizar saldo do usuário
$stmt = $pdo->prepare("
UPDATE users SET balance = balance + ?
WHERE id = (SELECT user_id FROM payments WHERE external_id = ?)
");
$stmt->execute([$data['amount'], $data['external_id']]);
// Confirmar agendamento se existir
$stmt = $pdo->prepare("
UPDATE appointments SET status = 'confirmed'
WHERE payment_id = ?
");
$stmt->execute([$data['external_id']]);
echo json_encode(['status' => 'success']);
} else {
echo json_encode(['status' => 'error']);
}
exit;
}
// Webhook de callback para saques (NikaPay)
if (isset($_GET['callback']) && $_GET['callback'] === 'withdrawal') {
header('Content-Type: application/json');
$json = file_get_contents('php://input');
$data = json_decode($json, true);
if ($data && isset($data['status'])) {
$stmt = $pdo->prepare("UPDATE withdrawals SET status = ? WHERE external_id = ?");
$stmt->execute([$data['status'], $data['external_id']]);
echo json_encode(['status' => 'success']);
} else {
echo json_encode(['status' => 'error']);
}
exit;
}
?>
Erro ao criar funil');
}
})
.catch(error => {
console.error('Erro:', error);
showError('Erro ao criar funil');
});
}
function editFunnel(id) {
// Implementar editor de funil
alert('Editor de funil em desenvolvimento');
}
function previewFunnel(id, slug) {
window.open(`?f=${id}/${slug}`, '_blank');
}
function copyFunnelLink(id, slug) {
const link = `${window.location.origin}${window.location.pathname}?f=${id}/${slug}`;
navigator.clipboard.writeText(link).then(() => {
showSuccess('Link copiado para a área de transferência!');
});
}
// Carregar funis
loadFunnels();
// Clientes específico
function loadClients() {
fetch('?ajax=clients_list')
.then(response => response.json())
.then(clients => {
const tbody = document.getElementById('clientsTable');
if (clients.length === 0) {
tbody.innerHTML = `
Nenhum cliente cadastrado
|
`;
return;
}
tbody.innerHTML = clients.map(client => `
${client.name} |
${client.phone || '-'} |
${client.email || '-'} |
${client.visits} |
${formatDate(client.created_at)} |
|
`).join('');
})
.catch(error => {
console.error('Erro ao carregar clientes:', error);
});
}
function viewClient(id) {
// Implementar visualização detalhada do cliente
alert('Visualização de cliente em desenvolvimento');
}
// Carregar clientes
loadClients();
// Agendamentos específico
function loadAppointments() {
fetch('?ajax=appointments_list')
.then(response => response.json())
.then(appointments => {
const tbody = document.getElementById('appointmentsTable');
if (appointments.length === 0) {
tbody.innerHTML = `
Nenhum agendamento encontrado
|
`;
return;
}
tbody.innerHTML = appointments.map(appointment => {
let statusBadge = '';
switch(appointment.status) {
case 'confirmed':
statusBadge = 'Confirmado';
break;
case 'pending':
statusBadge = 'Pendente';
break;
case 'cancelled':
statusBadge = 'Cancelado';
break;
default:
statusBadge = 'Desconhecido';
}
return `
${appointment.client_name}
${appointment.client_phone}
|
${appointment.service} |
${formatDate(appointment.appointment_date)} |
${appointment.appointment_time} |
${formatCurrency(appointment.amount)} |
${statusBadge} |
|
`;
}).join('');
})
.catch(error => {
console.error('Erro ao carregar agendamentos:', error);
});
}
function saveAppointment() {
const form = document.getElementById('newAppointmentForm');
const formData = new FormData(form);
formData.append('action', 'save_appointment');
fetch('', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
bootstrap.Modal.getInstance(document.getElementById('newAppointmentModal')).hide();
form.reset();
loadAppointments();
showSuccess('Agendamento salvo com sucesso!');
} else {
showError('