Neste tutorial, aprenderemos como criar um aplicativo “AI Quotes Generator” com JavaScript. Este aplicativo demonstrará como buscar dados da API OpenAI e gerar cotações envolventes para diferentes categorias ou ambientes personalizados.
Estrutura HTML
A estrutura HTML consistirá nos seguintes elementos:
- Um botão no canto superior direito abrirá um modal que permite aos usuários adicionar sua chave de API OpenAI.
- Uma entrada que permite aos usuários adicionar um clima personalizado
- Várias opções preenchidas com categorias
- Um botão que, ao ser clicado, gerará cotações da API OpenAI.
Como fizemos antes, usaremos a estrutura Bootstrap para fazer grande parte do trabalho pesado no que diz respeito à UI. A estrutura HTML ficará assim:
1 |
|
2 |
|
3 |
id="api" |
4 |
type="button" |
5 |
class="btn btn-primary" |
6 |
data-bs-toggle="modal" |
7 |
data-bs-target="#myModal" |
8 |
>
|
9 |
Add API Key |
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
class="modal fade" |
16 |
id="myModal" |
17 |
tabindex="-1" |
18 |
aria-labelledby="exampleModalLabel" |
19 |
aria-hidden="true" |
20 |
>
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
Your API Key remains stored locally in your browser |
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
type="text" class="form-control" id="apikey" />
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
type="button" |
37 |
class="btn btn-secondary" |
38 |
data-bs-dismiss="modal" |
39 |
>
|
40 |
Close |
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
Create the perfect quote based on your current mood.. |
55 |
|
56 |
|
57 |
id="input" |
58 |
name="mood" |
59 |
type="text" |
60 |
placeholder="Enter your current mood" |
61 |
class="form-control mb-4 mx-auto w-75 text-center" |
62 |
style="width: 60%; display: inline-block" |
63 |
/>
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
..or choose from our custom categories |
71 |
|
72 |
|
73 |
class="quotes row justify-content-center mt-8 mb-4" |
74 |
>
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
id="generate" |
84 |
class="generate-btn btn btn-primary" |
85 |
type="submit" |
86 |
>
|
87 |
Generate Quotes |
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
Preencheremos as categorias de cotação dinamicamente com JavaScript e, assim que obtivermos as cotações da API OpenAI, elas serão preenchidas no contêiner de resultados.
Para este projeto, também usaremos as ferramentas jQuery do Bootstrap para habilitar a funcionalidade modal. Inclua os links CDN no cabeçalho.
1 |
|
2 |
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous" />
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
Estilo CSS
Graças ao Bootstrap, não precisaremos de muitos estilos personalizados. Mas vamos adicionar os estilos CSS personalizados de que precisamos, incluindo a fonte web DM Mono.
1 |
@import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"); |
2 |
|
3 |
body { |
4 |
font-family: "DM Mono", monospace; |
5 |
}
|
6 |
.form-group { |
7 |
margin: 2rem 0; |
8 |
}
|
9 |
label { |
10 |
margin-bottom: 1rem; |
11 |
}
|
12 |
@media (min-width: 768px) { |
13 |
.category-wrapper { |
14 |
border-right: 2px solid #999; |
15 |
}
|
16 |
}
|
17 |
|
18 |
#loader, |
19 |
.message, |
20 |
.radio-group input[type="radio"]{ |
21 |
display: none; |
22 |
}
|
23 |
|
Armazenando API KEY no armazenamento local
Para que o aplicativo funcione conforme esperado, os usuários devem adicionar sua API KEY ao armazenamento local para garantir persistência e segurança. Para conseguir isso, temos o “Adicionar chave de API” botão no canto superior direito. O botão é configurado com o data-bs-target
atributo definido como myModal
indicando que clicar nele acionará o modal com o id “meuModal” a ser exibido.
1 |
|
2 |
id="api" |
3 |
type="button" |
4 |
class="btn btn-primary" |
5 |
data-bs-toggle="modal" |
6 |
data-bs-target="#myModal" |
7 |
>
|
8 |
Add API Key |
9 |
|
Assim que o modal for mostrado ao usuário, usaremos jQuery para anexar um ouvinte de evento para o shown.bs.modal
evento. Na função de evento, faremos o seguinte:
- Obtenha o valor da chave API do usuário.
- salve o valor no armazenamento local,
- ocultar o modal
1 |
$("#myModal").on("shown.bs.modal", function () { |
2 |
|
3 |
const saveButton = document.querySelector("#myModal .btn-primary"); |
4 |
const apiKeyInput = document.querySelector("#apikey"); |
5 |
|
6 |
saveButton.addEventListener("click", function () { |
7 |
const apiKeyValue = apiKeyInput.value; |
8 |
localStorage.setItem("API_KEY", apiKeyValue); |
9 |
$("#myModal").modal("hide"); |
10 |
});
|
11 |
});
|
Então, quando você clica no botão “Adicionar CHAVE DE API“, o modal será exibido assim:
A próxima etapa é definir e mapear nossas categorias personalizadas para a interface. Aqui estão as categorias. Sinta-se à vontade para definir o seu próprio.
1 |
const categorias = [ |
2 |
"motivation", |
3 |
"life", |
4 |
"hope", |
5 |
"funny", |
6 |
"love", |
7 |
"philosophy", |
8 |
"sadness", |
9 |
];
|
Vamos pegar o elemento com as aspas da classe que abrigará as categorias.
1 |
const quotes = document.querySelector(".quotes"); |
A seguir, usaremos o map()
método para gerar uma marcação HTML de um rótulo e um elemento de rádio de entrada para cada categoria; o elemento de entrada de rádio conterá o id
e value
da categoria, enquanto o rótulo conterá o name
da categoria.
1 |
const mappedCategories = categories.map((category) => { |
2 |
capitalizeText = category.charAt(0).toUpperCase() + category.slice(1); |
3 |
return ` |
4 |
type="radio"
|
5 |
class="btn-check"
|
6 |
name="mood"
|
7 |
id="${category}" |
8 |
value="${category}" |
9 |
autocomplete="off"
|
10 |
/>
|
11 |
|
12 |
class="btn btn-secondary align-items-center justify-content-center fs-5"
|
13 |
for="${category}" |
14 |
>${capitalizeText}`; |
15 |
});
|
16 |
quotes.innerHTML = mappedCategories.join(""); |
Agora o aplicativo está assim:
Configuração da API OpenAI
Implementamos a funcionalidade de adição de chave de API. Agora, vamos pegar a chave no site da OpenAI. Se você não tem uma conta, vá para o Site OpenAI e inscreva-se gratuitamente.
Depois de fornecer os detalhes necessários, navegue até o documentação. Clique em API KEY no canto superior esquerdo, crie sua chave de API, copie e cole a chave e armazene-a em um local seguro.
Usando a chave API
Crie uma função assíncrona chamada getData()
que leva dois parâmetros, a saber prompt
e API KEY
,
1 |
const getData = async (prompt, API_KEY) => { |
2 |
|
3 |
}
|
Dentro da função, queremos usar fetch()
função para fazer uma solicitação à API OpenAI e exibir a resposta gerada ao usuário. Dentro de getData()
função, adicione um try-catch
bloco com o seguinte código.
1 |
const getData = async (prompt, API_KEY) => { |
2 |
|
3 |
try { |
4 |
const response = await fetch("https://api.openai.com/v1/chat/completions", { |
5 |
method: "POST", |
6 |
headers: { |
7 |
Authorization: `Bearer ${API_KEY}`, |
8 |
"Content-Type": "application/json", |
9 |
},
|
10 |
body: JSON.stringify({ |
11 |
model: "gpt-3.5-turbo", |
12 |
mensagens: [ |
13 |
{
|
14 |
role: "user", |
15 |
content: `Generate 10 quotes about ${prompt}`, |
16 |
},
|
17 |
],
|
18 |
temperature: 0.7, |
19 |
}),
|
20 |
});
|
21 |
const data = await response.json(); |
22 |
|
23 |
return data; |
24 |
} catch (error) { |
25 |
|
26 |
return error; |
27 |
}
|
28 |
};
|
Este é todo o código que precisamos para obter dados do OpenAI. Vamos analisar o código.
- Nós usamos o
fetch()
função dentro do bloco try para fazer um assíncronoPOST
solicitação para o URL especificado. - No corpo da solicitação, especificamos
gpt-3.5-turbo
como modelo a ser utilizado; - A API OpenAI também espera um cabeçalho de autorização contendo o
API KEY,
e o corpo da solicitação deve ser umJSON
objeto contendo parâmetros como omodel
,prompt
etemperature
(indica aleatoriedade das respostas; valores mais altos indicam mais aleatoriedade das respostas). - Por fim, devolvemos o
response.json
objeto. Em caso de algum erro, também devolvemos oerror
objeto.
A resposta dos dados é assim;
Como você pode ver, os dados que precisamos exibir ao usuário estão contidos no arquivo choices
variedade. Os dados serão formatados antes de serem exibidos ao usuário.
O getData()
A função será chamada quando o usuário clicar no botão gerar cotações. Vamos adicionar um ouvinte de evento ao botão gerar.
1 |
const generateBtn = document.querySelector(".generate-btn"); |
2 |
generateBtn.addEventListener("click", async (e) => { |
3 |
e.preventDefault(); |
4 |
|
5 |
}
|
Quando ocorre o evento click, queremos executar uma função que faça o seguinte:
- Obtenha a API KEY do armazenamento local.
- Se nenhuma API KEY for encontrada no armazenamento local, exibiremos um erro, informando ao usuário que ele deve adicionar sua API KEY.
- Obtenha o prompt de uma categoria ou de um prompt personalizado.
- Passe o prompt e a APIKEY para o
getData()
função. - Mostre um controle giratório quando o aplicativo estiver buscando os dados.
- Depois de obter uma resposta, pare o botão giratório.
- Formate os dados e exiba-os em cartões bootstrap.
- Em caso de algum erro, exibiremos a mensagem de erro apropriada.
Atualize a função do ouvinte de eventos da seguinte maneira:
1 |
const generateBtn = document.querySelector(".generate-btn"); |
2 |
generateBtn.addEventListener("click", async (e) => { |
3 |
e.preventDefault(); |
4 |
const key = localStorage.getItem("API_KEY"); |
5 |
|
6 |
if (!key) { |
7 |
displayError("","Please add your OPENAI API Key, The KEY will be stored locally on your browser"); |
8 |
return; |
9 |
}
|
10 |
|
11 |
let prompt = ""; |
12 |
let radio = document.querySelector('input[name="mood"]:checked'); |
13 |
|
14 |
if (document.querySelector('input[name="mood"]:checked')) { |
15 |
radio = document.querySelector('input[name="mood"]:checked'); |
16 |
prompt = radio.value; |
17 |
} else { |
18 |
CustomInput = document.getElementById("input"); |
19 |
prompt = CustomInput.value; |
20 |
}
|
21 |
|
22 |
if (!prompt) { |
23 |
displayError(prompt,'Please choose a category or provide a custom mood"'); |
24 |
return; |
25 |
}
|
26 |
|
27 |
loader.style.display = "block"; |
28 |
|
29 |
const data = await getData(prompt, key); |
30 |
|
31 |
if (data.choices) { |
32 |
const container = document.getElementById("result"); |
33 |
// data from aync
|
34 |
const quotesArray = data.choices[0].message.content.split("\n"); |
35 |
const mappedArray = quotesArray.map((quote) => { |
36 |
const trimmedQuote = quote.replace(/^\d+\.|"$/g, "").trim(); |
37 |
|
38 |
return ` |
39 |
|
40 |
|
41 |
${trimmedQuote} |
42 |
|
43 |
|
44 |
`; |
45 |
});
|
46 |
|
47 |
container.innerHTML = mappedArray.join(""); |
48 |
|
49 |
} else { |
50 |
displayError("",data.error.message ) |
51 |
|
52 |
}
|
53 |
CustomInput.value = ""; |
54 |
});
|
Vamos detalhar o que está acontecendo acima:
- Primeiro, evitamos a natureza padrão do navegador definindo
e.preventDefault()
-
const key = localStorage.getItem("API_KEY");
obterá a API KEY do armazenamento local. - Se nenhuma chave for encontrada, passaremos uma mensagem de erro para o
displayError()
função. -
let prompt = "";
declara uma string vazia que armazenará o valor do prompt do usuário. -
if (document.querySelector('input[name="mood"]:checked')){...} :
verifica se o usuário selecionou uma categoria e se for verdade, o valor da entrada de rádio selecionada é atribuído ao prompt. - No
else
instrução, o prompt será o valor de entrada personalizado se o usuário tiver inserido um clima personalizado. - Se o valor do prompt for
null
ouundefined
significa que o usuário não forneceu um aviso (seja selecionando uma categoria de humor ou inserindo um clima personalizado), exibiremos uma mensagem de erro. - Após toda a validação ter passado, exibiremos um elemento giratório com
loader.style.display = "block";
getData()
Função
A seguir, chamaremos o wait getData()
função e passe o prompt e a chave API. Como a função é assíncrona, usamos await para garantir que a execução seja atrasada até que a busca de dados seja bem-sucedida.
Como vimos anteriormente, o objeto de dados retornado pela API se parece com isto:
O conteúdo que precisamos está contido no choices[0].message
objeto.
-
data.choices[0].message.content.split("\n");
cria uma matriz de cotações chamada quotesArray dividindo-a onde \n aparece. - O
quotesArray
agora contém todas as citações, tudo o que precisamos fazer é usar omap()
método e para cada cotação, remova qualquer espaço em branco à esquerda ou à direita comquote.replace(/^\d+\.|"$/g, "").trim();
e retorne uma marcação HTML para cada cotação representada por um cartão Bootstrap. - Por fim, definimos o
innerHTML
doquotes
contêiner à marcação HTML concatenada de cada cotação. - Em caso de erro da API,
displayError("",data.error.message
) exibe a mensagem de erro retornada.
Manipulação de erros
Em vez de repetir o processo de tratamento de erros, o displayError()
função irá lidar com isso. Leva o valueText
e um messageText
como parâmetros, verifica se o valueText
é nulo ou indefinido e exibe a mensagem de erro contida em messageText
.
A mensagem de erro é exibida por 4 segundos e depois ocultada do usuário.
1 |
function displayError(valueText,messageText) { |
2 |
const message = document.querySelector(".message"); |
3 |
if (valueText === "") { |
4 |
|
5 |
message.textContent = messageText; |
6 |
message.style.display = "block"; |
7 |
}
|
8 |
setTimeout(() => { |
9 |
message.textContent = ""; |
10 |
message.style.display = "none"; |
11 |
}, 4000); |
12 |
return; |
13 |
}
|
Para a limpeza final, vamos garantir que se o usuário começar a digitar no campo de entrada personalizado, qualquer categoria selecionada anteriormente (por meio de botões de opção) será desmarcada.
1 |
const inputField = document.getElementById("input"); |
2 |
inputField.addEventListener("input", () => { |
3 |
const radio = document.querySelector('input[name="mood"]:checked'); |
4 |
if (radio) { |
5 |
radio.checked = false; |
6 |
}
|
7 |
});
|
Conclusão
Uau! Foi muito para absorver, mas o resultado final vale a pena e você terá aprendido muito. Abordamos as etapas necessárias para criar seu próprio aplicativo AI Quote Generator com JavaScript. Agora você pode integrar IA ao seu aplicativo para gerar conteúdo exclusivo!
Vamos nos lembrar do produto final.