Lucas & Matheus

Digite a senha para acessar a lista.

true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 20, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36', CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_HTTPHEADER => [ 'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8', ], ]); $conteudo = curl_exec($ch); $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $erro = curl_error($ch); curl_close($ch); if ($conteudo === false || $httpCode >= 400 || $erro) { return null; } return is_string($conteudo) ? $conteudo : null; } function urlAbsoluta(string $baseUrl, string $url): ?string { $url = trim($url); if ($url === '') { return null; } if (preg_match('/^https?:\/\//i', $url)) { return $url; } if (strpos($url, '//') === 0) { $scheme = parse_url($baseUrl, PHP_URL_SCHEME) ?: 'https'; return $scheme . ':' . $url; } $base = parse_url($baseUrl); if (!$base || empty($base['host'])) { return null; } $scheme = $base['scheme'] ?? 'https'; $host = $base['host']; $port = isset($base['port']) ? ':' . $base['port'] : ''; $path = $base['path'] ?? '/'; if (strpos($url, '/') === 0) { return $scheme . '://' . $host . $port . $url; } $dir = rtrim(str_replace('\\', '/', dirname($path)), '/'); if ($dir === '.') { $dir = ''; } return $scheme . '://' . $host . $port . $dir . '/' . $url; } function construirUrlBuscaLoja(string $loja, string $termo): ?string { $q = urlencode($termo); $mapa = [ 'Mercado Livre' => 'https://lista.mercadolivre.com.br/' . str_replace('%20', '-', $q), 'Shopee' => 'https://shopee.com.br/search?keyword=' . $q, 'Amazon' => 'https://www.amazon.com.br/s?k=' . $q, 'Magalu' => 'https://www.magazineluiza.com.br/busca/' . rawurlencode($termo) . '/', ]; return $mapa[$loja] ?? null; } function extrairImagensDaBuscaHtml(string $html, string $baseUrl): array { $candidatas = []; libxml_use_internal_errors(true); $dom = new DOMDocument(); @$dom->loadHTML($html); $xpath = new DOMXPath($dom); $metaQueries = [ "//meta[@property='og:image']/@content", "//meta[@name='twitter:image']/@content", "//meta[@property='twitter:image']/@content", ]; foreach ($metaQueries as $query) { $nodes = $xpath->query($query); if ($nodes) { foreach ($nodes as $node) { $img = urlAbsoluta($baseUrl, trim((string)$node->nodeValue)); if ($img) { $candidatas[] = $img; } } } } $imgNodes = $xpath->query("//img"); if ($imgNodes) { foreach ($imgNodes as $imgNode) { $atributos = [ 'src', 'data-src', 'data-lazy', 'data-lazy-src', 'data-zoom', 'data-image', 'data-img', 'data-srcset', 'data-old-hires', 'data-original', ]; foreach ($atributos as $atributo) { $valor = trim((string)$imgNode->getAttribute($atributo)); if ($valor === '') { continue; } if ($atributo === 'data-srcset') { $partes = explode(',', $valor); foreach ($partes as $parte) { $trecho = trim($parte); if ($trecho !== '') { $tmp = preg_split('/\s+/', $trecho); if (!empty($tmp[0])) { $img = urlAbsoluta($baseUrl, $tmp[0]); if ($img) { $candidatas[] = $img; } } } } } else { $img = urlAbsoluta($baseUrl, $valor); if ($img) { $candidatas[] = $img; } } } $srcset = trim((string)$imgNode->getAttribute('srcset')); if ($srcset !== '') { $partes = explode(',', $srcset); foreach ($partes as $parte) { $trecho = trim($parte); if ($trecho !== '') { $tmp = preg_split('/\s+/', $trecho); if (!empty($tmp[0])) { $img = urlAbsoluta($baseUrl, $tmp[0]); if ($img) { $candidatas[] = $img; } } } } } } } if (preg_match_all('/https?:\\\\?\/\\\\?\/[^"\']+\.(jpg|jpeg|png|webp)(\?[^"\']*)?/i', $html, $matches)) { foreach ($matches[0] as $match) { $img = str_replace(['\\/', '\\u002F'], ['/', '/'], $match); $img = preg_replace('/\\\\u0026/i', '&', $img); if ($img) { $candidatas[] = $img; } } } if (preg_match_all('/(?:\/|\.\/)[^"\']+\.(jpg|jpeg|png|webp)(\?[^"\']*)?/i', $html, $matchesRel)) { foreach ($matchesRel[0] as $match) { $img = urlAbsoluta($baseUrl, str_replace(['\\/', '\\u002F'], ['/', '/'], $match)); if ($img) { $candidatas[] = $img; } } } $candidatas = array_map(function ($img) { $img = html_entity_decode($img, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $img = str_replace(['\\/', '\\u002F'], ['/', '/'], $img); return trim($img); }, $candidatas); $candidatas = array_values(array_unique(array_filter($candidatas, function ($img) { if (!preg_match('/^https?:\/\//i', $img)) { return false; } if (preg_match('/\.(svg|gif)(\?|$)/i', $img)) { return false; } if (preg_match('/logo|icon|sprite|avatar|favicon|banner|tracking|pixel/i', $img)) { return false; } return true; }))); return $candidatas; } function extrairImagensPorNome(string $termo): array { $urlBusca = 'https://duckduckgo.com/?q=' . urlencode($termo); $html = baixarConteudoUrl($urlBusca); if (!$html || !preg_match('/vqd=([\'"])(.*?)\1/', $html, $match)) { return []; } $vqd = $match[2]; $urlApi = 'https://duckduckgo.com/i.js?l=br-pt&o=json&q=' . urlencode($termo) . '&vqd=' . $vqd . '&f=,,,&p=1'; $json = baixarConteudoUrl($urlApi); if (!$json) { return []; } $dados = json_decode($json, true); if (!isset($dados['results'])) { return []; } $imagens = []; foreach ($dados['results'] as $item) { $img = $item['image'] ?? ''; if ( $img && preg_match('/^https?:\/\//', $img) && !preg_match('/logo|icon|avatar|sprite/i', $img) ) { $imagens[] = [ 'url' => $img, 'loja' => 'Web' ]; } } $unicos = []; foreach ($imagens as $item) { $unicos[$item['url']] = $item; } return array_slice(array_values($unicos), 0, 20); } function salvarImagemRemota(string $urlImagem): ?string { if (!filter_var($urlImagem, FILTER_VALIDATE_URL)) { return null; } $ch = curl_init($urlImagem); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 25, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36', CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_HTTPHEADER => [ 'Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: ' . $urlImagem, ], ]); $conteudo = curl_exec($ch); $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $contentType = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE); curl_close($ch); if ($conteudo === false || $httpCode >= 400 || !$conteudo) { return null; } $mimeParaExt = [ 'image/jpeg' => 'jpg', 'image/jpg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', ]; $tipoBase = strtolower(trim(explode(';', $contentType)[0])); $extensao = $mimeParaExt[$tipoBase] ?? null; if ($extensao === null) { $finfo = new finfo(FILEINFO_MIME_TYPE); $mimeReal = $finfo->buffer($conteudo); $extensao = $mimeParaExt[$mimeReal] ?? null; } if ($extensao === null) { $path = parse_url($urlImagem, PHP_URL_PATH) ?: ''; $extPath = strtolower(pathinfo($path, PATHINFO_EXTENSION)); if (in_array($extPath, ['jpg', 'jpeg', 'png', 'webp'], true)) { $extensao = $extPath === 'jpeg' ? 'jpg' : $extPath; } } if ($extensao === null) { return null; } $novoNome = 'upload_' . uniqid('', true) . '.' . $extensao; $destino = __DIR__ . '/' . $novoNome; if (file_put_contents($destino, $conteudo) === false) { return null; } return $novoNome; } function removerAcentos(string $texto): string { $convertido = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $texto); return $convertido !== false ? $convertido : $texto; } function chaveOrdenacaoNome(string $nome): string { $nome = trim($nome); $semPrefixo = preg_replace('/^[^[:alpha:]]+/u', '', $nome); if (!is_string($semPrefixo) || $semPrefixo === '') { $semPrefixo = $nome; } $semPrefixo = removerAcentos($semPrefixo); if (function_exists('mb_strtolower')) { $semPrefixo = mb_strtolower($semPrefixo, 'UTF-8'); } else { $semPrefixo = strtolower($semPrefixo); } return trim($semPrefixo); } function montarUrlFiltro(string $categoria, string $ordem): string { return '?' . http_build_query([ 'categoria' => $categoria, 'ordem' => $ordem, ]); } if (isset($_GET['acao']) && $_GET['acao'] === 'buscar_imagens_nome') { header('Content-Type: application/json; charset=utf-8'); $termo = trim($_GET['termo'] ?? ''); if ($termo === '') { echo json_encode([ 'ok' => false, 'mensagem' => 'Digite o nome do produto primeiro.', 'imagens' => [], ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } $imagens = extrairImagensPorNome($termo); $imagens = array_slice($imagens, 0, 10); echo json_encode([ 'ok' => count($imagens) > 0, 'mensagem' => count($imagens) > 0 ? '' : 'Não consegui localizar imagens automaticamente para esse nome. Tente um nome mais específico ou use galeria/câmera.', 'imagens' => $imagens, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } $categoriasValidas = ['Todos', 'Prioritários', 'Sala', 'Cozinha', 'Quarto', 'Banheiro', 'Outros']; $categoriasBanco = ['Sala', 'Cozinha', 'Quarto', 'Banheiro', 'Outros']; $lojasSugestao = ['Mercado Livre', 'Shopee', 'Amazon', 'Magalu']; $filtro = $_GET['categoria'] ?? 'Todos'; $ordem = $_GET['ordem'] ?? 'az'; if (!in_array($filtro, $categoriasValidas, true)) { $filtro = 'Todos'; } $ordensValidas = ['az', 'recentes']; if (!in_array($ordem, $ordensValidas, true)) { $ordem = 'az'; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $acao = $_POST['acao'] ?? ''; if ($acao === 'adicionar') { $nome = trim($_POST['nome'] ?? ''); $loja = trim($_POST['loja'] ?? ''); $categoria = trim($_POST['categoria'] ?? 'Outros'); $valor = normalizarValorBanco($_POST['valor'] ?? ''); $prioritario = isset($_POST['prioritario']) ? 1 : 0; if ( $nome !== '' && in_array($categoria, $categoriasBanco, true) && $valor > 0 ) { $foto = salvarImagemUpload($_FILES['foto_arquivo'] ?? null); if ($foto === null) { $fotoUrlRemota = trim($_POST['foto_url_remota'] ?? ''); if ($fotoUrlRemota !== '') { $foto = salvarImagemRemota($fotoUrlRemota); } } $stmt = $pdo->prepare(" INSERT INTO itens (nome, loja, categoria, valor, foto, comprado, prioritario, data_cadastro, data_alteracao) VALUES (:nome, :loja, :categoria, :valor, :foto, 0, :prioritario, NOW(), NOW()) "); $stmt->execute([ 'nome' => $nome, 'loja' => $loja !== '' ? $loja : null, 'categoria' => $categoria, 'valor' => $valor, 'foto' => $foto, 'prioritario' => $prioritario, ]); } header('Location: index.php' . montarUrlFiltro($filtro, $ordem)); exit; } if ($acao === 'editar') { $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; $nome = trim($_POST['nome'] ?? ''); $loja = trim($_POST['loja'] ?? ''); $categoria = trim($_POST['categoria'] ?? 'Outros'); $valor = normalizarValorBanco($_POST['valor'] ?? ''); $prioritario = isset($_POST['prioritario']) ? 1 : 0; if ( $id > 0 && $nome !== '' && in_array($categoria, $categoriasBanco, true) && $valor > 0 ) { $stmtBusca = $pdo->prepare("SELECT foto FROM itens WHERE id = :id LIMIT 1"); $stmtBusca->execute(['id' => $id]); $itemAtual = $stmtBusca->fetch(); $foto = $itemAtual['foto'] ?? null; $novaFoto = salvarImagemUpload($_FILES['foto_arquivo'] ?? null); if ($novaFoto === null) { $fotoUrlRemota = trim($_POST['foto_url_remota'] ?? ''); if ($fotoUrlRemota !== '') { $novaFoto = salvarImagemRemota($fotoUrlRemota); } } if ($novaFoto !== null) { if ($foto && file_exists(__DIR__ . '/' . $foto)) { unlink(__DIR__ . '/' . $foto); } $foto = $novaFoto; } $stmt = $pdo->prepare(" UPDATE itens SET nome = :nome, loja = :loja, categoria = :categoria, valor = :valor, foto = :foto, prioritario = :prioritario, data_alteracao = NOW() WHERE id = :id "); $stmt->execute([ 'id' => $id, 'nome' => $nome, 'loja' => $loja !== '' ? $loja : null, 'categoria' => $categoria, 'valor' => $valor, 'foto' => $foto, 'prioritario' => $prioritario, ]); } header('Location: index.php' . montarUrlFiltro($filtro, $ordem)); exit; } if ($acao === 'excluir') { $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; if ($id > 0) { $stmtBusca = $pdo->prepare("SELECT foto FROM itens WHERE id = :id LIMIT 1"); $stmtBusca->execute(['id' => $id]); $item = $stmtBusca->fetch(); if ($item && !empty($item['foto'])) { $caminhoFoto = __DIR__ . '/' . $item['foto']; if (file_exists($caminhoFoto)) { unlink($caminhoFoto); } } $stmt = $pdo->prepare("DELETE FROM itens WHERE id = :id"); $stmt->execute(['id' => $id]); } header('Location: index.php' . montarUrlFiltro($filtro, $ordem)); exit; } if ($acao === 'comprar') { $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; $valorPago = normalizarValorBanco($_POST['valor_pago'] ?? ''); if ($id > 0 && $valorPago > 0) { $stmt = $pdo->prepare(" UPDATE itens SET comprado = 1, valor_pago = :valor_pago WHERE id = :id "); $stmt->execute([ 'id' => $id, 'valor_pago' => $valorPago, ]); } header('Location: index.php' . montarUrlFiltro($filtro, $ordem)); exit; } if ($acao === 'desmarcar') { $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; if ($id > 0) { $stmt = $pdo->prepare(" UPDATE itens SET comprado = 0, valor_pago = NULL WHERE id = :id "); $stmt->execute(['id' => $id]); } header('Location: index.php' . montarUrlFiltro($filtro, $ordem)); exit; } } $stmtTodos = $pdo->query("SELECT * FROM itens"); $todosItens = $stmtTodos->fetchAll(); $contagemCategorias = [ 'Todos' => count($todosItens), 'Prioritários' => 0, 'Sala' => 0, 'Cozinha' => 0, 'Quarto' => 0, 'Banheiro' => 0, 'Outros' => 0, ]; foreach ($todosItens as $itemContagem) { $cat = (string)($itemContagem['categoria'] ?? ''); if (isset($contagemCategorias[$cat])) { $contagemCategorias[$cat]++; } if ((int)($itemContagem['prioritario'] ?? 0) === 1) { $contagemCategorias['Prioritários']++; } } if ($filtro === 'Todos') { $itens = $todosItens; } elseif ($filtro === 'Prioritários') { $itens = array_values(array_filter($todosItens, function (array $item): bool { return (int)($item['prioritario'] ?? 0) === 1; })); } else { $itens = array_values(array_filter($todosItens, function (array $item) use ($filtro): bool { return (string)($item['categoria'] ?? '') === $filtro; })); } if ($ordem === 'recentes') { usort($itens, function (array $a, array $b): int { $dataA = strtotime((string)($a['data_alteracao'] ?? $a['data_cadastro'] ?? '')) ?: 0; $dataB = strtotime((string)($b['data_alteracao'] ?? $b['data_cadastro'] ?? '')) ?: 0; if ($dataA !== $dataB) { return $dataB <=> $dataA; } return (int)($b['id'] ?? 0) <=> (int)($a['id'] ?? 0); }); } else { usort($itens, function (array $a, array $b): int { $chaveA = chaveOrdenacaoNome((string)($a['nome'] ?? '')); $chaveB = chaveOrdenacaoNome((string)($b['nome'] ?? '')); $comparacao = strcmp($chaveA, $chaveB); if ($comparacao !== 0) { return $comparacao; } return strcmp( removerAcentos(trim((string)($a['nome'] ?? ''))), removerAcentos(trim((string)($b['nome'] ?? ''))) ); }); } $itensPendentes = array_values(array_filter($itens, function (array $item): bool { return (int)($item['comprado'] ?? 0) !== 1; })); $itensComprados = array_values(array_filter($itens, function (array $item): bool { return (int)($item['comprado'] ?? 0) === 1; })); $stmtTotais = $pdo->query(" SELECT COALESCE(SUM(CASE WHEN comprado = 0 THEN valor ELSE 0 END), 0) AS total_pendente, COALESCE(SUM(CASE WHEN comprado = 1 THEN COALESCE(valor_pago, valor) ELSE 0 END), 0) AS total_comprado, COALESCE(SUM(CASE WHEN comprado = 0 AND COALESCE(prioritario, 0) = 1 THEN valor ELSE 0 END), 0) AS total_prioridades FROM itens "); $totais = $stmtTotais->fetch(); function renderizarCardItem(array $item, string $filtroAtual, string $ordemAtual): void { $plan = (float)$item['valor']; $pago = $item['valor_pago'] !== null ? (float)$item['valor_pago'] : $plan; $diff = $pago - $plan; $dadosVisualizacao = [ 'nome' => $item['nome'], 'categoria' => $item['categoria'], 'loja' => $item['loja'] ?? '', 'valorPlanejado' => formatarMoeda((float)$item['valor']), 'valorPago' => $item['valor_pago'] !== null ? formatarMoeda((float)$item['valor_pago']) : '', 'comprado' => (int)$item['comprado'], 'prioritario' => (int)($item['prioritario'] ?? 0), 'foto' => (!empty($item['foto']) && file_exists(__DIR__ . '/' . $item['foto'])) ? $item['foto'] : '' ]; $nomeBusca = htmlspecialchars((string)$item['nome'], ENT_QUOTES); ?>
Prioritário
Loja:
Plan:
Pago:
0): ?>
+ (Acima do previsto)
- (Economizou)
No alvo 🎯
Lucas & Matheus
Sair

Lucas & Matheus

Falta Comprar
Já Comprado
Prioridades
Nenhum item adicionado ainda. Clique em + para começar!

Pendentes

Nenhum item pendente nesta visualização.

Comprados

Nenhum item comprado nesta visualização.