<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexandre Justen Filho</title>
    <description>The latest articles on DEV Community by Alexandre Justen Filho (@alexandrejusten).</description>
    <link>https://dev.to/alexandrejusten</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1262521%2Fa2a4964d-b744-4122-9b06-b471a987fe47.jpeg</url>
      <title>DEV Community: Alexandre Justen Filho</title>
      <link>https://dev.to/alexandrejusten</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexandrejusten"/>
    <language>en</language>
    <item>
      <title>Database-per-tenant {Explicando Padrões de arquitetura de banco de dados multilocatários}</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Fri, 03 Oct 2025 01:43:45 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/database-per-tenant-explicando-padroes-de-arquitetura-de-banco-de-dados-multilocatarios-2kpj</link>
      <guid>https://dev.to/alexandrejusten/database-per-tenant-explicando-padroes-de-arquitetura-de-banco-de-dados-multilocatarios-2kpj</guid>
      <description>&lt;h2&gt;
  
  
  Arquiteturas de Banco de Dados para Multi-Tenancy
&lt;/h2&gt;

&lt;p&gt;=================================================&lt;/p&gt;

&lt;p&gt;Quando comecei a desenvolver a versão web de um sistema que já rodava localmente, me deparei com uma decisão estratégica: &lt;strong&gt;como estruturar o banco de dados para suportar vários clientes (multi-tenancy)&lt;/strong&gt;. Essa escolha não é apenas técnica; ela impacta escalabilidade, segurança, custos e até a forma como a equipe vai trabalhar no dia a dia.&lt;/p&gt;

&lt;p&gt;Grosso modo, existem três modelos principais: desde o mais simples, onde todos compartilham as mesmas tabelas, até o mais isolado, com um banco dedicado para cada cliente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Padrão 1 – Banco Único e Tabelas Compartilhadas
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE customers (
  id INT PRIMARY KEY,
  tenant_id INT NOT NULL,
  name VARCHAR(255),
  email VARCHAR(255),
  INDEX(tenant_id)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Nesse modelo, todos os clientes dividem as mesmas tabelas. O que diferencia os dados é uma coluna &lt;code&gt;tenant_id&lt;/code&gt;. É o desenho mais direto e geralmente o primeiro que vem à mente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vantagens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Mais barato e simples de operar.&lt;/li&gt;
&lt;li&gt;  Backups, monitoramento e atualizações em um único ponto.&lt;/li&gt;
&lt;li&gt;  Mudanças de schema refletem em todos os clientes de uma vez.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Desvantagens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Qualquer descuido em queries pode vazar dados entre clientes.&lt;/li&gt;
&lt;li&gt;  Um cliente pesado pode afetar todos os outros.&lt;/li&gt;
&lt;li&gt;  Não dá muita flexibilidade de customização.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Padrão 2 – Banco Único, Schemas Separados
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE SCHEMA tenant1;
CREATE TABLE tenant1.customers (...);

CREATE SCHEMA tenant2;
CREATE TABLE tenant2.customers (...);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Aqui ainda temos um banco só, mas cada cliente fica em um schema separado. É algo que bancos como &lt;strong&gt;PostgreSQL&lt;/strong&gt; e &lt;strong&gt;SQL Server&lt;/strong&gt; oferecem nativamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vantagens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Mais isolamento do que o modelo anterior.&lt;/li&gt;
&lt;li&gt;  Possibilita pequenas customizações por cliente.&lt;/li&gt;
&lt;li&gt;  Custos ainda controlados (um único banco físico).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Desvantagens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  As migrações precisam ser replicadas em cada schema.&lt;/li&gt;
&lt;li&gt;  Com muitos clientes, o banco pode atingir limites de objetos.&lt;/li&gt;
&lt;li&gt;  Gerenciar backups/restores é mais trabalhoso.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Padrão 3 – Um Banco por Cliente
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE DATABASE tenant1;
CREATE DATABASE tenant2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esse é o modelo mais isolado: cada cliente tem sua própria base de dados, totalmente separada das demais.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vantagens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Isolamento total (ideal para compliance e segurança).&lt;/li&gt;
&lt;li&gt;  Escalabilidade independente por cliente.&lt;/li&gt;
&lt;li&gt;  Facilidade em atender exigências regulatórias.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Desvantagens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Maior custo de operação e infraestrutura.&lt;/li&gt;
&lt;li&gt;  Migrações de schema são mais trabalhosas.&lt;/li&gt;
&lt;li&gt;  Exige automação para gerenciar conexões e atualizações.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparativo Rápido
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Critério&lt;/th&gt;
&lt;th&gt;Compartilhar Banco + Esquema&lt;/th&gt;
&lt;th&gt;Compartilhar Banco + Schemas&lt;/th&gt;
&lt;th&gt;Banco por Tenant&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Suporte em SGDBs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Todos&lt;/td&gt;
&lt;td&gt;PostgreSQL, SQL Server, etc.&lt;/td&gt;
&lt;td&gt;Todos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conformidade&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Difícil&lt;/td&gt;
&lt;td&gt;Possível com esforço extra&lt;/td&gt;
&lt;td&gt;Mais simples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexidade&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baixa&lt;/td&gt;
&lt;td&gt;Média/Alta&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customização&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limitada&lt;/td&gt;
&lt;td&gt;Moderada&lt;/td&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Escalabilidade&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Banco inteiro&lt;/td&gt;
&lt;td&gt;Banco inteiro&lt;/td&gt;
&lt;td&gt;Por cliente&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Dicas para Migrações de Schema
&lt;/h2&gt;

&lt;p&gt;Não importa o modelo que você escolha, mudar a estrutura do banco será sempre uma dor de cabeça se não houver disciplina.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Versione seus scripts&lt;/strong&gt; no Git com numeração sequencial.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Automatize&lt;/strong&gt; com CI/CD, rodando testes antes de aplicar.&lt;/li&gt;
&lt;li&gt;  Escreva scripts &lt;strong&gt;idempotentes&lt;/strong&gt; (seguros para rodar mais de uma vez).&lt;/li&gt;
&lt;li&gt;  Aplique mudanças &lt;strong&gt;primeiro em staging&lt;/strong&gt; e depois em subset de clientes.&lt;/li&gt;
&lt;li&gt;  Mantenha &lt;strong&gt;compatibilidade retroativa&lt;/strong&gt; sempre que possível.&lt;/li&gt;
&lt;li&gt;  Use um catálogo centralizado para acompanhar versões de cada tenant.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Não existe receita única. O melhor modelo depende do seu estágio e das exigências do seu negócio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Quer começar simples e barato? → &lt;strong&gt;Banco + Tabelas Compartilhadas&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Precisa de isolamento e compliance? → &lt;strong&gt;Banco por Cliente&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Busca um equilíbrio? → &lt;strong&gt;Banco único com Schemas separados&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O mais importante é escolher algo que funcione bem &lt;em&gt;hoje&lt;/em&gt;, mas com clareza de como migrar no futuro conforme sua base de clientes crescer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Tianzhou 18 de março de 2025 – &lt;a href="https://www.bytebase.com/blog/multi-tenant-database-architecture-patterns-explained/" rel="noopener noreferrer"&gt;Multi-Tenant Database Architecture Patterns Explained&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  turso – &lt;a href="https://turso.tech/multi-tenancy" rel="noopener noreferrer"&gt;Database Multi-Tenancy Made Easy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>saas</category>
    </item>
    <item>
      <title>GitHub em 1 Clique: De Público para Privado em Segundos!🔒</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Sat, 01 Feb 2025 02:26:12 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/github-em-1-clique-de-publico-para-privado-em-segundos-knl</link>
      <guid>https://dev.to/alexandrejusten/github-em-1-clique-de-publico-para-privado-em-segundos-knl</guid>
      <description>&lt;p&gt;Você já quis alterar rapidamente um repositório público para privado no GitHub sem precisar navegar pelas configurações? Este script em Node.js permite fazer isso diretamente pelo terminal, em apenas um clique!&lt;/p&gt;

&lt;p&gt;Para contextualizar: fui contratado como professor de desenvolvimento de sistemas e, ao montar meus planos de aula, percebi que tinha muitos códigos e algoritmos no meu GitHub que seriam úteis durante as aulas. Como alguns alunos já estão me procurando nas redes sociais, percebi que deveria tornar meus repositórios importantes privados.&lt;/p&gt;

&lt;p&gt;Quando comecei essa tarefa, achei que seria fácil, mas depois de 10 repositórios, ainda faltavam 53, kkkkk. Então, achei mais prudente resolver isso com um script simples, e foi isso que fiz.&lt;/p&gt;

&lt;p&gt;Primeiro passo: Obter sua API token.&lt;/p&gt;

&lt;p&gt;Acesse o link: &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;https://github.com/settings/tokens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você pode gerar um token, que será muito importante para termos acesso e modificar a visibilidade do repositório.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jb82uvu9c282a78lrzw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jb82uvu9c282a78lrzw.png" alt="Image description" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você deve marcar a opção "repo".&lt;/p&gt;

&lt;p&gt;Pronto! Agora que você tem a chave, vamos ao código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const axios = require('axios');
const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

async function ask(query) {
  return new Promise(resolve =&amp;gt; rl.question(query, resolve));
}

async function getRepositories(username, token) {
  try {
    const response = await axios.get(`https://api.github.com/users/${username}/repos`, {
      headers: { Authorization: `Bearer ${token}` }
    });

    return response.data.map(repo =&amp;gt; repo.name);
  } catch (error) {
    console.error('Erro ao buscar repositórios');
    return [];
  }
}

async function visibility(username, token, repoName) {
  try {
    await axios.patch(`https://api.github.com/repos/${username}/${repoName}`, 
      { private: true }, 
      { headers: { Authorization: `Bearer ${token}` } }
    );
    console.log(`O repositório ${repoName} foi privado`);
  } catch (error) {
    console.error('Erro ao alterar visibilidade');
  }
}

async function main() {
  const username = await ask('Nome de usuário do GitHub: ');
  const token = await ask('Digite sua token: ');

  const repos = await getRepositories(username, token);

  if (repos.length === 0) {
    console.log('Nenhum repositório encontrado');
    rl.close();
    return;
  }

  repos.forEach((repo, index) =&amp;gt; console.log(`${index + 1}. ${repo}`));

  const choice = await ask('\nDigite o número do repositório que deseja tornar privado: ');
  const selectedRepo = repos[parseInt(choice) - 1];

  if (selectedRepo) {
    await visibility(username, token, selectedRepo);
  } else {
    console.log('Escolha inválida.');
  }

  rl.close();
}

main();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resumo do Fluxo do Código&lt;br&gt;
O usuário fornece seu nome de usuário e token do GitHub.&lt;br&gt;
O script faz uma requisição para listar os repositórios do usuário.&lt;br&gt;
O script exibe os repositórios disponíveis e pede ao usuário para escolher um.&lt;br&gt;
O script altera a visibilidade do repositório escolhido, tornando-o privado.&lt;br&gt;
O script exibe uma mensagem de confirmação ou erro dependendo do resultado.&lt;br&gt;
Esse código usa axios para fazer as requisições HTTP e readline para interação com o usuário, criando uma solução simples, mas eficaz, para alterar rapidamente a visibilidade de múltiplos repositórios no GitHub.&lt;/p&gt;

&lt;p&gt;Se esse código foi útil para você, deixe um comentário ou curta! Muito obrigado por ler até aqui.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>javascript</category>
      <category>development</category>
    </item>
    <item>
      <title>Transforme Seu Servidor Local em um Site Público com Ngrok</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Thu, 31 Oct 2024 02:20:43 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/transforme-seu-servidor-local-em-um-site-publico-com-ngrok-30do</link>
      <guid>https://dev.to/alexandrejusten/transforme-seu-servidor-local-em-um-site-publico-com-ngrok-30do</guid>
      <description>&lt;p&gt;Quando desenvolvemos uma aplicação web localmente e queremos compartilhá-la para testes ou demonstrações, encontramos uma barreira comum: a aplicação só está acessível em nossa rede local. É aqui que entra o ngrok!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O Que é Tunelamento?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O tunelamento permite expor um servidor local para a internet através de um canal seguro. Isso é especialmente útil em ambientes de desenvolvimento, onde você deseja compartilhar seu servidor com clientes ou testá-lo em outros dispositivos sem ter que fazer o deploy completo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por Que Usar o Ngrok?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O ngrok é uma das ferramentas mais populares para tunelamento, e por boas razões:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rápido e Fácil de Usar: Com apenas uma linha de comando, você pode criar um túnel para o seu servidor local.&lt;/li&gt;
&lt;li&gt;Segurança: Cria uma conexão segura (HTTPS) automaticamente.&lt;/li&gt;
&lt;li&gt;Flexível: Funciona com HTTP, HTTPS, TCP e até WebSockets.&lt;/li&gt;
&lt;li&gt;Insights e Depuração: Permite monitorar e depurar requisições recebidas em tempo real.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Como Usar o Ngrok?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Baixe e Instale o Ngrok:&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Acesse ngrok.com e baixe a versão adequada para o seu sistema.&lt;/p&gt;

&lt;p&gt;Após o download, descompacte o arquivo e coloque o executável em uma pasta de fácil acesso.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Autenticação (opcional, mas recomendado):&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crie uma conta no ngrok e copie seu token de autenticação.&lt;br&gt;
No terminal, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok authtoken SEU_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjw82o3v6d7s7j4nkcen6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjw82o3v6d7s7j4nkcen6.png" alt="Image description" width="403" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Iniciando o Túnel:&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com o servidor da sua aplicação rodando (por exemplo, na porta 3000), inicie o túnel com o comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdlwbs70wf3foel9slmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdlwbs70wf3foel9slmn.png" alt="Image description" width="800" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este comando cria um túnel HTTP e HTTPS para a porta 3000 do seu localhost. Você verá um endereço público no formato &lt;a href="https://xxxx.ngrok.io" rel="noopener noreferrer"&gt;https://xxxx.ngrok.io&lt;/a&gt; que pode ser acessado de qualquer lugar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Visualize e Depure o Tráfego:&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Acesse &lt;a href="http://localhost:4040" rel="noopener noreferrer"&gt;http://localhost:4040&lt;/a&gt; para ver informações detalhadas sobre cada requisição recebida, incluindo cabeçalhos, payload e status de resposta. Essa visualização é ótima para depurar APIs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Encerrando o Túnel:&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para parar o ngrok, basta fechar o terminal ou pressionar Ctrl+C.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Exemplos de Uso do Ngrok&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Testes de Webhooks: Ideal para desenvolver e testar webhooks de serviços externos como Stripe, PayPal e GitHub, sem precisar fazer o deploy do código.&lt;/li&gt;
&lt;li&gt;Demonstrar Aplicações em Tempo Real: Compartilhe seu progresso com colegas ou clientes em uma aplicação em execução localmente.&lt;/li&gt;
&lt;li&gt;Desenvolvimento de APIs: Teste e depure endpoints localmente antes de enviá-los para produção.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O ngrok é uma ferramenta indispensável para quem desenvolve aplicações web e precisa de acesso rápido, seguro e fácil a um servidor local a partir da internet. Com ele, o tunelamento se torna simples, ampliando as possibilidades de desenvolvimento e teste.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ngrok</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>🐍Como Importar Dados do Excel para SQLite com Python em Poucas Linhas</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Wed, 30 Oct 2024 22:13:11 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/como-importar-dados-do-excel-para-sqlite-com-python-em-poucas-linhas-o7b</link>
      <guid>https://dev.to/alexandrejusten/como-importar-dados-do-excel-para-sqlite-com-python-em-poucas-linhas-o7b</guid>
      <description>&lt;p&gt;Neste post, vou ensinar como transferir dados de uma planilha Excel diretamente para um banco de dados SQLite usando apenas algumas linhas de código em Python. Muito útil para quem precisa centralizar dados em um banco de dados, especialmente para uso em aplicações que fazem consultas frequentes, como dashboards ou ferramentas de análise!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;&lt;strong&gt;Bibliotecas:&lt;/strong&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pandas as pd
import sqlite3

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;&lt;strong&gt;Carregar o Arquivo Excel:&lt;/strong&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arquivo_excel = './teste.xlsx'
df = pd.read_excel(arquivo_excel)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;&lt;strong&gt;Conectar ao Banco de Dados SQLite:&lt;/strong&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conexao = sqlite3.connect('database.db')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;&lt;strong&gt;Exportar os Dados para o SQLite:&lt;/strong&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.to_sql('tabela_excel', conexao, if_exists='replace', index=False)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;&lt;strong&gt;Fechar a Conexão e Confirmação:&lt;/strong&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conexao.close()
print("Dados importados com sucesso!")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dessa forma em poucas linhas de código temos a nossa tabela xlsx exportada para um banco SQLite!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmphub82vfjhqmnxryuq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmphub82vfjhqmnxryuq.png" alt="Image description" width="379" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28bqe9mj7w7v5k9uuqkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28bqe9mj7w7v5k9uuqkh.png" alt="Image description" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Análise de Sulco de Pneus com Inteligência Artificial em Python!</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Wed, 30 Oct 2024 21:16:09 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/analise-de-sulco-de-pneus-com-inteligencia-artificial-em-python-3kmi</link>
      <guid>https://dev.to/alexandrejusten/analise-de-sulco-de-pneus-com-inteligencia-artificial-em-python-3kmi</guid>
      <description>&lt;p&gt;A análise de sulcos de pneus é uma tarefa crucial para identificar o desgaste e garantir a segurança, especialmente em veículos que percorrem longas distâncias. Com o uso de Inteligência Artificial (IA) e Python, podemos automatizar esse processo de maneira rápida e precisa. Aqui, mostramos como um modelo de rede neural convolucional (CNN), baseado na arquitetura VGG16, classifica pneus em "novos" ou "usados", enquanto o OpenCV ajuda a analisar as imagens para medir a profundidade dos sulcos.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tecnologias Usadas&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python:&lt;br&gt;
Linguagem de programação popular para IA e Machine Learning, especialmente por suas bibliotecas avançadas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenCV: &lt;br&gt;
Usado para processar as imagens, detectar contornos e medir a área dos sulcos dos pneus.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TensorFlow e Keras: &lt;br&gt;
Bibliotecas de aprendizado profundo. Utilizamos o Keras para trabalhar com o modelo VGG16, uma CNN pré-treinada para reconhecimento de imagens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Matplotlib: &lt;br&gt;
Biblioteca para visualização de dados e criação de gráficos, tornando os resultados da classificação mais interpretáveis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Código:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1. Carregar e Pré-processar as Imagens:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
 As imagens dos pneus são carregadas e redimensionadas para um formato padrão (150x150 pixels) necessário para a entrada do modelo. Esse redimensionamento mantém a proporção e normaliza os valores dos pixels entre 0 e 1 para facilitar o processamento pelo modelo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import cv2
import numpy as np
from tensorflow.keras.applications.vgg16 import preprocess_input

def process_image(image_path, target_size=(150, 150)):
    image = cv2.imread(image_path)
    if image is None:
        print(f"Erro ao carregar a imagem: {image_path}. Verifique o caminho e a integridade do arquivo.")
        return None, None

    image_resized = cv2.resize(image, target_size, interpolation=cv2.INTER_AREA)
    image_array = np.array(image_resized) / 255.0  
    image_array = np.expand_dims(image_array, axis=0)
    image_preprocessed = preprocess_input(image_array)

    return image_resized, image_preprocessed

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;2. Classificação com o Modelo Treinado:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Carregamos um modelo de rede neural convolucional pré-treinado, que foi ajustado para classificar pneus como "novos" ou "usados". Esse modelo fornece uma pontuação de confiança que indica a probabilidade de um pneu ser novo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from tensorflow.keras.models import load_model

model = load_model('pneu_classificador.keras')
prediction = model.predict(image_preprocessed)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;3. Análise dos Contornos para Profundidade de Sulco:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
A detecção da profundidade do sulco é realizada com técnicas de visão computacional. A imagem em escala de cinza passa por um filtro de borrão e pela detecção de bordas com Canny, o que ajuda a identificar contornos dos sulcos. Em seguida, calculamos a área total dos contornos, o que nos permite estimar o desgaste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def detect_tread_depth(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blurred, 30, 100)
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    total_area = sum(cv2.contourArea(c) for c in contours if cv2.contourArea(c) &amp;gt; 100)
    return total_area

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;4. Visualização e Análise de Resultados:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Após a classificação e análise de cada pneu, os resultados são exibidos com Matplotlib. Comparamos a pontuação de confiança da classificação e a área dos sulcos detectada em cada imagem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import matplotlib.pyplot as plt

confidence_scores = []
total_area_green_values = []
predicted_classes = []

for image_file in os.listdir(ver_dir):
    image_path = os.path.join(ver_dir, image_file)
    image_resized, image_preprocessed = process_image(image_path)
    if image_preprocessed is not None:
        prediction = model.predict(image_preprocessed)
        confidence_score = prediction[0][0]
        total_area_green = detect_tread_depth(image_resized)

        predicted_class = "novo" if total_area_green &amp;gt; 500 else "usado"
        confidence_scores.append(confidence_score)
        total_area_green_values.append(total_area_green)
        predicted_classes.append(predicted_class)

        plt.imshow(cv2.cvtColor(image_resized, cv2.COLOR_BGR2RGB))
        plt.title(f"Pneu {predicted_class} (Área: {total_area_green:.2f}, Confiança: {confidence_score:.2f})")
        plt.axis('off')
        plt.show()

fig, axs = plt.subplots(2, 1, figsize=(10, 10))

axs[0].bar(os.listdir(ver_dir), confidence_scores, color='skyblue')
axs[0].set_title('Confiança na Classificação')
axs[0].set_ylim(0, 1)
axs[0].tick_params(axis='x', rotation=45)

axs[1].bar(os.listdir(ver_dir), total_area_green_values, color='lightgreen')
axs[1].set_title('Área Verde Detectada')
axs[1].tick_params(axis='x', rotation=45)

plt.tight_layout()
plt.show()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5k65vwl2aaapusvu0gi9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5k65vwl2aaapusvu0gi9.png" alt="Image description" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft92xh1ag4p96h39zpzjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft92xh1ag4p96h39zpzjr.png" alt="Image description" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9ydk4s1mqvgiy8hwb2e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9ydk4s1mqvgiy8hwb2e.png" alt="Image description" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esse meu projeto demonstra como é possível automatizar a análise do desgaste de pneus utilizando IA e visão computacional, resultando em uma classificação precisa e rápida. A arquitetura VGG16 e o uso de OpenCV são fundamentais para combinar a precisão do modelo de rede neural com a análise visual dos sulcos. Esse sistema pode ser expandido para monitoramento contínuo em frotas de veículos, ajudando a reduzir acidentes e otimizar o gerenciamento de pneus.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Criando um component &lt;Select/&gt; com opção de Search e Paginação para ReactNative-NativeBase</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Fri, 16 Feb 2024 14:00:27 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/criando-um-component-com-opcao-de-search-e-paginacao-para-reactnative-nativebase-4e6c</link>
      <guid>https://dev.to/alexandrejusten/criando-um-component-com-opcao-de-search-e-paginacao-para-reactnative-nativebase-4e6c</guid>
      <description>&lt;p&gt;Se você trabalha com React Native utilizando o NativeBase e muitas outras bibliotecas, e está precisando de um componente  que lhe dê a opção de pesquisa e paginação, provavelmente está decepcionado! Pois nativamente você não tem essa opção. No entanto, criei esse componente e gostaria de compartilhar com vocês. Então, vamos lá.&lt;/p&gt;

&lt;p&gt;Nativamente o component  do NativeBase vai ser assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxovjkvt7tvi3ccloe6lr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxovjkvt7tvi3ccloe6lr.png" alt="Image description" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Um componente  simples que pode servir para muitos casos, mas não para todos! O meu componente se comporta de forma diferente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu94tga2x9eiktkn62att.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu94tga2x9eiktkn62att.png" alt="Image description" width="389" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos observar que ele conta com a opção de pesquisa e também, ao chegar perto do final da lista, ele já faz a paginação, carregando um indicador de carregamento e, posteriormente, adicionando os itens da próxima página na minha lista. Por que não trazer todos os dados sem paginação? Basicamente, porque podemos ter centenas de milhares de dados e isso causaria problemas com a memória.&lt;br&gt;
Esse componente foi desenvolvido para ser utilizado em vários locais da minha aplicação, então ele não é específico para uma API ou um objeto só. Por isso, você pode utilizá-lo adaptando mínimos detalhes para que ele funcione na sua aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import { Box, Modal, Button, VStack, Text, View } from 'native-base';
import SearchComponent from './search-fields.utils';
import { FlatList } from 'native-base';
import { HStack } from 'native-base';
import { TouchableOpacity } from 'react-native';
import { ActivityIndicator } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome5';
import { useTheme } from 'native-base';

interface Props {
    apiUrl: any;
    searchName: string;
    valueName: string;
    placeholder?: string;
    isRequired?: boolean;
    value?: any;
    onChange: (value: any) =&amp;gt; void;
}

function SelectPaginate(props: Props) {
    const { colors } = useTheme();
    const [modalVisible, setModalVisible] = React.useState(false);
    const [selectValue, setSelectValue] = useState('');
    const [data, setData] = useState&amp;lt;any[]&amp;gt;([]);
    const [loading, setLoading] = useState(false);
    const [page, setPage] = useState(1);
    const [searchQuery, setSearchQuery] = useState&amp;lt;string&amp;gt;('');
    const [value, setValue] = useState();
    const perPage = 10;

    const onPress = (value: any) =&amp;gt; {
        setSelectValue(value?.[props.valueName]);
        setModalVisible(false);
        props.onChange(value);
    };

    const handleSearch = value =&amp;gt; {
        setSearchQuery(value);
    };

    useEffect(() =&amp;gt; {
        if (props.value !== undefined &amp;amp;&amp;amp; props.value !== null) {
            setSelectValue(props.value?.[props.valueName]);
        }
    }, [props.value]);

    useEffect(() =&amp;gt; {
        if (data.length === 0) {
            load()
        } else {
            null;
        }
    }, [data]);

    useEffect(() =&amp;gt; {
        setPage(1)
        setData([]);
    }, [searchQuery]);

    function areArraysEqual(arr1, arr2) {
        if (arr1.length !== arr2.length) {
            return false;
        }

        for (let i = 0; i &amp;lt; arr1.length; i++) {
            if (arr1[i] !== arr2[i]) {
                return false;
            }
        }

        return true;
    }

    async function loadApi() {
        if (loading) return;

        setLoading(true);
        try {
            const response = await axios.get(`${props.apiUrl}`, {
                params: {
                    page: page - 1,
                    size: perPage,
                    sort: 'id,DESC',
                    cacheBuster: new Date().getTime(),
                },
            });
            if (!areArraysEqual(data, response.data)) {
                setData([...data, ...response.data]);
                setPage(page+1);
            } else {
                return;
            }
        } catch (error) {
            console.log(error);
            return null;
        } finally {
            setLoading(false);
        }
    }

    async function loadApiSearch() {

        try {
            const response = await axios.get(`${props.apiUrl}`, {
                params: {
                    page: 0,
                    size: perPage,
                    sort: 'id,DESC',
                    [props.searchName]: searchQuery,
                    cacheBuster: new Date().getTime(),
                },
            });

            if (!areArraysEqual(data, response.data)) {
                setData([...data, ...response.data]);
                setPage(page+1);
            } else {
                return ;
            }
        } catch (error) {
            console.log(error);
            return null;
        } finally {
            setLoading(false);
        }
    }

    async function load(){
        if (uniqueData.length &amp;lt; 10 &amp;amp;&amp;amp; uniqueData.length &amp;gt;0) {
            return;
        }
            if (searchQuery === '' || searchQuery === undefined || searchQuery === null) {

                loadApi();

            } else {
                setLoading(true);
                loadApiSearch();
            }

    }

    function FooterList({ Load }) {
        if (!Load) return null;
        return (
            &amp;lt;View&amp;gt;
                &amp;lt;ActivityIndicator size={25} color="#121212" /&amp;gt;
            &amp;lt;/View&amp;gt;
        );
    }
    function ListItem({ data }) {
        return (
            &amp;lt;Box pl={['0', '4']} pr={['0', '5']} py="2"&amp;gt;
                &amp;lt;TouchableOpacity
                    onPress={() =&amp;gt; {
                        onPress(data);
                    }}&amp;gt;
                    &amp;lt;HStack space={[2, 3]} justifyContent="space-between"&amp;gt;
                        &amp;lt;VStack&amp;gt;
                            &amp;lt;Text
                                _dark={{
                                    color: 'warmGray.50',
                                }}
                                color="coolGray.800"
                                fontSize={15}
                                mt={2}&amp;gt;
                                {data?.[props.valueName]}
                            &amp;lt;/Text&amp;gt;
                        &amp;lt;/VStack&amp;gt;
                    &amp;lt;/HStack&amp;gt;
                &amp;lt;/TouchableOpacity&amp;gt;
            &amp;lt;/Box&amp;gt;
        );
    }
    function TextComponent() {
        if (selectValue === null || selectValue === '') {
            return &amp;lt;Text style={{ color: 'grey' }}&amp;gt;{props.placeholder}&amp;lt;/Text&amp;gt;;
        } else {
            return &amp;lt;Text style={{ color: 'white' }}&amp;gt;{selectValue}&amp;lt;/Text&amp;gt;;
        }
    }
    const uniqueData = Array.from(new Set(data.map(item =&amp;gt; item.id)))
    .map(id =&amp;gt; data.find(item =&amp;gt; item.id === id));
    return (
        &amp;lt;&amp;gt;
            &amp;lt;Modal isOpen={modalVisible} onClose={() =&amp;gt; setModalVisible(false)} avoidKeyboard justifyContent="flex-end" bottom="0" size="lg"&amp;gt;
                &amp;lt;Modal.Content&amp;gt;
                    &amp;lt;Modal.CloseButton /&amp;gt;
                    &amp;lt;Modal.Header&amp;gt;
                        &amp;lt;SearchComponent onSearch={handleSearch} placeholder={props.placeholder} colorText={'grey'} /&amp;gt;
                    &amp;lt;/Modal.Header&amp;gt;
                    &amp;lt;FlatList
                        height="500"
                        data={uniqueData}
                        renderItem={({ item }) =&amp;gt; &amp;lt;ListItem data={item} /&amp;gt;}
                        keyExtractor={item =&amp;gt; item.id}
                        onEndReached={load}
                        onEndReachedThreshold={0.1}
                        ListFooterComponent={&amp;lt;FooterList Load={loading} /&amp;gt;}
                        ml={2}
                    /&amp;gt;
                &amp;lt;/Modal.Content&amp;gt;
            &amp;lt;/Modal&amp;gt;
            &amp;lt;VStack space={8} alignItems="center" &amp;gt;
                &amp;lt;Button
                    onPress={() =&amp;gt; {
                        setModalVisible(!modalVisible);
                    }}
                    style={{ borderWidth: 1, borderColor: colors.white50, width: '100%'}} h={12} &amp;gt;
                    &amp;lt;TextComponent /&amp;gt;
                    &amp;lt;Icon size={18} name={'chevron-down'} style={{ textAlign: 'right', marginLeft: '94%', marginTop: '-6%', color: 'gray' }} /&amp;gt;
                &amp;lt;/Button&amp;gt;
            &amp;lt;/VStack&amp;gt;
        &amp;lt;/&amp;gt;
    );
}

export default SelectPaginate;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você também vai precisar do componente de pesquisa, que é importado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import {  Input,  Box } from 'native-base';

function SearchComponent({ onSearch, placeholder ,colorText ='white'}) {
    const [value, setValue] = useState("");

    useEffect(() =&amp;gt; {
      const timer = setTimeout(() =&amp;gt; {
        onSearch(value);
      }, 500);

      return () =&amp;gt; {
        clearTimeout(timer);
      };
    }, [value, onSearch]);

    const handleChange = (text) =&amp;gt; {
      setValue(text);
    };

  return (
    &amp;lt;Box alignItems="center"&amp;gt;
      &amp;lt;Input
      color={colorText}
      value={value}
      onChangeText={handleChange}
      placeholder={placeholder}
      w="40"
    /&amp;gt;
    &amp;lt;/Box&amp;gt;
  );
}

export default SearchComponent;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lembre-se de adaptar esses componentes para a sua aplicação, como a requisição para a API e o objeto. Tente entender a interface criada para passar os valores corretos. Em caso de dúvidas, basta comentar neste post que eu ajudo você.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>selectcomponen</category>
      <category>searchreact</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Trabalhando com 2 pc's no setup,Dicas!🖥️</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Tue, 23 Jan 2024 14:31:36 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/trabalhando-com-2-pcs-no-setupdicas-1e</link>
      <guid>https://dev.to/alexandrejusten/trabalhando-com-2-pcs-no-setupdicas-1e</guid>
      <description>&lt;p&gt;Bom, se você trabalha com dois computadores no seu setup e não possui periféricos extras, às vezes isso pode se tornar um problema, até mesmo uma dor de cabeça! Atualmente, no meu ambiente de trabalho, tenho um desktop e um notebook, cada um com sua função específica. Muitas vezes, eu me via enrolado nos emaranhados de cabos para conseguir trabalhar, precisando desconectar o mouse e o teclado de um USB para conectá-los em outro. Isso incluía também o fone de ouvido... acredite, não era uma situação muito agradável.&lt;/p&gt;

&lt;p&gt;Atualmente, tenho alguns periféricos que me ajudam a resolver esse problema, e vou compartilhar essa dica com você.&lt;/p&gt;

&lt;p&gt;A primeira solução que posso oferecer são equipamentos sem fio, como fones, teclados e mouses. No entanto, no Brasil, periféricos sem fio de qualidade costumam ser caros, e nem sempre oferecem o melhor tempo de resposta. Além disso, a questão da bateria pode causar inconvenientes inesperados, como acabar durante uma reunião ou partida, o que não seria nada agradável, não é mesmo? (risos)&lt;/p&gt;

&lt;p&gt;Recentemente, tenho utilizado dois principais switches: HDMI e USB.&lt;/p&gt;

&lt;p&gt;Como funcionam? Basicamente, o switch HDMI possui duas entradas e uma saída, sendo perfeito para alternar entre as telas do meu monitor principal. Com apenas um clique, a imagem passa do meu notebook para o meu PC e vice-versa. Recomendo o Baseus bidirectional HDMI, que além do design perfeito, já está comigo há dois anos e não tenho do que reclamar.&lt;/p&gt;

&lt;p&gt;Quanto ao switch USB, utilizo o Ugreen Hub KVM Switch USB 3.0, que possui até quatro entradas. Da mesma forma que o HDMI, basta um clique para mudar esses quatro periféricos de entrada para o outro PC. Eles custam cerca de R$114 o switch HDMI e R$249 o USB, mas eu garanto que será um bom investimento.&lt;/p&gt;

&lt;p&gt;Agora, resolvemos o problema do HDMI e alguns periféricos como mouse, teclado, câmera... mas e a saída de áudio? Ainda será necessário ficar trocando? A resposta é: depende! Você pode investir em uma interface de áudio USB, como a Behringer UM2. Embora atualmente tenha um custo elevado, proporcionará uma melhor qualidade de áudio. Conectada ao switch USB, basta um clique para direcionar seu microfone e fone de ouvido para o computador selecionado.&lt;/p&gt;

&lt;p&gt;Basicamente, é isso! Essa foi a solução que encontrei para resolver meu problema no setup, e espero que também ajude você.&lt;/p&gt;

</description>
      <category>setup</category>
      <category>working</category>
      <category>tips</category>
      <category>community</category>
    </item>
    <item>
      <title>Gerando Arquivo XLSX(excel) no React Native! json to XLSX</title>
      <dc:creator>Alexandre Justen Filho</dc:creator>
      <pubDate>Mon, 22 Jan 2024 18:17:11 +0000</pubDate>
      <link>https://dev.to/alexandrejusten/gerando-arquivo-xlsxexcel-no-react-native-json-to-xlsx-4hh6</link>
      <guid>https://dev.to/alexandrejusten/gerando-arquivo-xlsxexcel-no-react-native-json-to-xlsx-4hh6</guid>
      <description>&lt;p&gt;Bom, provavelmente, se você chegou a este post, está precisando salvar um arquivo xlsx (Excel) através do seu aplicativo móvel! Nos últimos dias, enfrentei o mesmo problema e tentei, por diversas vezes, o caminho errado. Segue aqui como você deve proceder.&lt;/p&gt;

&lt;p&gt;O primeiro passo é fazer com que sua API retorne um JSON! No meu caso, eu tinha uma API que me retornava diretamente um xlsx, e isso não é um problema quando estamos trabalhando na web, devido ao DOM dos nossos navegadores. No entanto, não é esse o assunto que iremos abordar.&lt;/p&gt;

&lt;p&gt;Nossa API deve ter um retorno como:&lt;br&gt;
&lt;code&gt;[{ "id": '1', "dado": 'primeiro dado' }, { "id": '2', "dado": 'segundo dado' }];&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Um JSON bem simples, não importa o número de objetos, nem mesmo as propriedades, basta estar organizado.&lt;/p&gt;

&lt;p&gt;Precisaremos instalar duas bibliotecas: react-native-fs e xlsx.&lt;/p&gt;

&lt;p&gt;NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i xlsx react-native-fs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A partir desse momento, vou considerar que o seu response está salvo em 'data', contendo o JSON solicitado:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const data = [{ "id": '1', "dado": 'primeiro dado' }, { "id": '2', "dado": 'segundo dado' }];&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Será necessário adicionar as permissões em:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android/app/src/main/AndroidManifest.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adicione os seguintes códigos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /&amp;gt;
&amp;lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Função para criar o arquivo xlsx e salvá-lo no seu dispositivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const exportDataToExcel = () =&amp;gt; {

const data = [{ "id": '1', "dado": 'primeiro dado' }, { "id": '2', "dado": 'segundo dado' }];

            // criando o xlsx
            let sample_data_to_export = data;

            let wb = XLSX.utils.book_new();
            let ws = XLSX.utils.json_to_sheet(sample_data_to_export)
            XLSX.utils.book_append_sheet(wb, ws, "Users")
            const wbout = XLSX.write(wb, { type: 'binary', bookType: "xlsx" });

            //salvando o arquivo
            const fileName = 'arquivo.xlsx';
            const downloadDir = RNFS.DownloadDirectoryPath;
            const filePath = `${downloadDir}/${fileName}`;

            try {
                RNFS.writeFile(filePath,  wbout, 'ascii')
                console.log("sucesso,arquivo baixado em", filePath);
            } catch (error) {
                console.log('Erro ao baixar o arquivo:', error);

            }

        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto, agora basta chamar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exportDataToExcel();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lembre-se! Caso seu emulador não tenha o Excel, você pode transferir o arquivo para o PC usando.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adb pull /sdcard/arquivo.xlsx %USERPROFILE%\Desktop\
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será parecido com isto:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5vbr4g154l8zw1473v7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5vbr4g154l8zw1473v7.png" alt="Image description" width="256" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>xlsx</category>
      <category>mobile</category>
      <category>json</category>
    </item>
  </channel>
</rss>
