Minha evolução em 3 semestres
Nessa semana finalizei o curso técnico de 3 semestres no IFSP, em cada um desses semestres acabei focando em fazer uma função pra me testar e ver o que conseguia. Agora que finalizei o curso queria compartilhar minha evolução atráves de 3 funções/classes para vocês.
1° semestre - caixa para respostas do console
Na matéria de lógica da programação o professor acabou passando os exercicios do pythonBrasil para fazer no Replit (exercicos que fiz na época)
Quando aprendi sobre funções acabei inventando demais e gastei dezenas de horas pra fazer uma função que coloca a resposta da questão em uma caixa de linha. Na época foi um desafio e tanto e ela acabou ficando meio ruinzinha.
exemplo de uso
//para formatar saida em caixas
const saida = require('../funções').saida;
let bloco = []
//o que vai ter em cada linha
let paia = [
"[0-25] tem: ",
"[26-50] tem: ",
"[51-75] tem: ",
"[76-100] tem: "
]
//
let cont = 1
function entrada(){
let paia = Number(prompt("N"+cont+": "))
cont++
return paia
}
let a = [0,0,0,0]
for(let i = entrada(); i>=0; i = entrada()){
if(i >= 0 && i<= 25) a[0]++
else if(i >= 26 && i<= 50) a[1]++
else if(i >= 51 && i<= 75) a[2]++
else if(i >= 76 && i<= 100) a[3]++
}
console.log("\n quantos numeros estão no intervalo: ")
saida(bloco, 0, paia, 1, a[0], a[1], a[2], a[3])
saida(bloco, "stop", paia, 1)
/// NO CONSOLE ///
42. Faça um programa que leia uma quantidade indeterminada de números positivos e conte quantos
deles estão nos seguintes intervalos: [0-25], [26-50], [51-75] e [76-100]. A entrada de dados
deve terminar quando for lido um número negativo.
N1: > 2
N2: > 3
N3: > 6
N4: > -1
quantos numeros estão no intervalo:
.-------------------.
| [0-25] tem: 3 |
| [26-50] tem: 0 |
| [51-75] tem: 0 |
| [76-100] tem: 0 |
'-------------------'
Função:
function saida(bloco, stop, conteudo_format, linhas, titulo, cod, a, b){
let l = [
conteudo_format[0]+titulo,
conteudo_format[1]+cod,
conteudo_format[2]+a,
conteudo_format[3]+b,
]
if(titulo != undefined ) titulo = titulo.toString()
if(cod != undefined ) cod = cod.toString()
if(a != undefined ) a = a.toString()
if(b != undefined ) b = b.toString()
let all = [titulo, cod, a, b]
//para mostrar os blocos no final
if(stop == "stop"){
for(let i = 1; i<=linhas; i ++) {
for(let j = 0; j<=5; j++){
console.log(bloco[i][ ("linha"+j) ])
}
}
return
}
//criar os objetos das linhas se n tiverem sido criadas
if (bloco[linhas] == undefined) {
bloco[linhas] = {
linha0: "",
linha1: "",
linha2: "",
linha3: "",
linha4: "",
linha5: "",
}
}
//o tamanho da retangulo
let tamanho = 0, barras = "-"
for(let i = 0; i<4; i++){
let paiadnv = 0
if(all[i] != undefined) paiadnv = all[i]
if(paiadnv.length > 10) {if(paiadnv.length > tamanho){tamanho = paiadnv.length}}
else if(all[i] != undefined) tamanho = l[i].length
}
tamanho += 2
for(let i = 0; i<tamanho; i++) barras += "-"
//criar um retangulo para as infos
let format = {
f1: function(a) {bloco[linhas]["linha"+a] += "| "+l[0].padEnd(tamanho, " ") + "| "},
f2: function(b) {bloco[linhas]["linha"+b] += "| "+l[1].padEnd(tamanho, " ") + "| " },
f3: function(c) {bloco[linhas]["linha"+c] += "| "+l[2].padEnd(tamanho, " ") + "| "},
f4: function(d) {bloco[linhas]["linha"+d] += "| "+l[3].padEnd(tamanho, " ") + "| "},
}
let format_puro = {
f1: function(a) {bloco[linhas]["linha"+a] += "| "+titulo.padEnd(tamanho, " ") + "| "},
f2: function(b) {bloco[linhas]["linha"+b] += "| "+cod.padEnd(tamanho, " ") + "| " },
f3: function(c) {bloco[linhas]["linha"+c] += "| "+a.padEnd(tamanho, " ") + "| "},
f4: function(d) {bloco[linhas]["linha"+d] += "| "+b.padEnd(tamanho, " ") + "| "},
}
//verifica se a variavel está vazia e se não estiver formata a informação para o bloco
bloco[linhas].linha0 += "."+barras+". "
for(let i = 1, j = 1; i<=5; i++){
if(all[i-1] != undefined) {
if(all[i-1].length < 10) {format["f"+i](j); j++}
else {format_puro["f"+i](j); j++}
}
if(i == 5) {
bloco[linhas]["linha"+(j)] += "'"+barras+"' "
// for(let a = j+1; a<= 5; a++) bloco[linhas]["linha"+a] = "".padEnd(tamanho, " ")
}
}
}
function entrada(vezes, vetor, pergunta, paia, a){
for(i = 0; i<vezes; i++){
if(paia == 0) vetor[i] = prompt(pergunta+(i+1)+": ")
else if(paia == 1) vetor[i][a] = prompt(pergunta+(i+1)+": ")
else vetor[i] = Number(prompt(pergunta+(i+1)+": "))
}
}
module.exports = {saida, entrada}
No final eu só usei ela 2 vezes porque era chato usar e limitada
2° Semestre - Classe de Inputs (código aberração)
No projeto integrado do semestre 2° Semestre - Era para fazer um sistema para empresas de onibus com admin, catraca e site público - acabei esbarrando em validação de input e tive a ideia de fazer uma classe para reutlizar em todos os form do projeto. O problema é que era a primeira vez fazendo e a classe virou um monstro...
Os métodos eram muito especifico e acabavam sendo raros ser usado e o método principal que é de validação... tem 300 linhas com nomes de variaveis confusos. Eu criei um monstro e na época não sabia que refatora-la era a melhor opção
exemplo de uso
const cadastrar = new Inputs("#formscadastro");
cadastrar.allvalidacao({
cpf: {
max: 14,
min: 14,
caractereNpermitido: ['Nletra', 'Nacentuacao'],
pattern: [
"(\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2,5})",
// "\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}",
"cpf valido",
"cpf invalido",
],
autopontuar: [/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4", /[.-]/g, ""],
customErro: [
[/(\d{3}\.\d{3}\.\d{3}-\d{3,6})/, "não existe cpf com mais de 11 numeros"]
],
customEvento: [
(async (value) => {
if (((await axios.get("http://localhost:9000/api/user/cadastrar/?cpf=" + value)).data.resultado.cpf)) {
return "Esse CPF já está cadastrado"
}
})
],
btnoff: "required"
},
email: {
max: 64,
pattern: [
"^[a-zA-Z0-9\\._+\\-]+@[a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,6}$",
"email valido",
"email invalido",
],
customEvento: [(async (value) => {
if (((await axios.get("http://localhost:9000/api/user/cadastrar/?email=" + value)).data.resultado.email)) {
return "Esse email já está em uso"
}
})],
btnoff: "required"
},
});
const cadastro = cadastrar.cadastrar("http://localhost:9000/api/user/cadastrar", null,
erro = () => {},
success = ({
data
}) => {
localStorage.setItem("email", data)
location.href = "/login"
})
})
A classe em si
Não seria uma boa ideia colocar 400 linhas de código seco aqui, então para quem quiser ver o código inteiro está aqui: Pj2-G10-TechPass/frontend/public/js/validacao.js
um trecho do classe:
// ele retorna um objeto com todos os valores do input do site
allValues(modificarFormData = {}) {
const formobj = new FormData(this.forms);
const valoresPuro = {};
this.inputs(({ name, value }) => {
if (value !== "") {
valoresPuro[name] = value;
}
});
// modificarFormData é um objeto composto de metodos para modificar os valores do input
for (const key in modificarFormData) {
if (Object.hasOwnProperty.call(modificarFormData, key)) {
const valor = formobj.get(key);
const valorFormatado = modificarFormData[key](valor);
if (valorFormatado && valorFormatado !== "") {
formobj.set(key, valorFormatado);
valoresPuro[key] = valorFormatado;
}
}
}
const keysNull = [];
formobj.forEach((value, key) => {
if (!value || value.size === 0) keysNull.push(key);
});
keysNull.forEach((key) => {
formobj.delete(key);
});
for (const [_, value] of formobj.entries()) {
console.log(_, value);
if (typeof value == "object") {
return formobj;
}
}
// modificarFormData é um objeto composto de metodos para modificar os valores do input
return valoresPuro;
}
3° Semestre - Classe de Inputs para React Native
No projeto integrado do último semestre - Era para criar um app mobile, totem/admin, backend e um site estático para um parque da minha cidade, a apresentação saiu até como noticia no site da cidade - resolvi fazer uma classe para validacao de input também. E bem... depois de refatorar 4 vezes saiu algo aceitavel.
Fiz essa classe pra cuidar exclusivamente do lógica de post e validacão. Quando tentei estilizar o input nela a classe ficou muito grande e confusa, então acabei refatorando abstraindo todos os estilos para ser passados como props. A classe também usa o useApi (um hook que junta react query com outro hook responsavel por criar uma instancia axios com a lógica de token/refresh token) para lidar com a requisição para a API.
Classe:
const TIMEOUT_VALIDY = 500;
export class FormPaia<C extends string, Inp extends InputMinimoProps> {
public values = useRef({} as ObjC<C, string>).current;
protected useApiPaia: UseMutation<typeof this.values>["result"];
constructor(
private options: Options<C>,
private InputType: InputMinimo<Inp>,
private TemplateInput: ComponentType<TemplateInputProps>
) {
this.useApiPaia = useApi("mutate", options.submitOptions);
}
/*
* função para submeter o formulário
*/
public submit = () => this.useApiPaia.mutate(this.values);
/*
* componente que rerenderiza o que estiver dentro com as informações da tentativa de submissão
*/
public Escutar = ({ children: C }: EscutarPropsPaia<C>) => (
<C mutation={this.useApiPaia} />
);
/*
* Componente do Input com toda lógica de validação
*/
public Input = ({
campo,
defaultValue,
...props
}: Omit<Inp, "value" | "onChangeText"> & { campo: C }) => {
const [valueInput, setValuesInput] = useState(
this.values[campo] || defaultValue || ""
);
const infoValidy = this.useValid(valueInput, campo);
const { isPending } = this.useApiPaia;
if (valueInput) this.values[campo] = valueInput;
return (
<this.TemplateInput campo={campo} {...infoValidy}>
<this.InputType
editable={!isPending}
selectTextOnFocus={!isPending}
placeholder={campo}
{...(props as unknown as Inp)}
value={valueInput}
onChangeText={(v) => setValuesInput(v)}
/>
</this.TemplateInput>
);
};
/**
* Hook que cuida de validar os valores do input
*/
private useValid(value: string, campo: C) {
const [validy, setValidy] = useState<StateValues<typeof State>>(
State.EMPTY
);
useEffect(() => {
setValidy(State.LOADING);
const validadoresDoCampo = this.options.campos?.[campo];
if (!validadoresDoCampo) return setValidy(State.EMPTY); // não existe validacao para esse campo
const timeoutId = setTimeout(async () => {
for await (const [verify, avisoDoErro] of validadoresDoCampo) {
const isValidy = await verify(value);
const isValidyMsg = typeof isValidy === "string" && isValidy;
if (isValidy) {
return setValidy(isValidyMsg || avisoDoErro); // ERROR
}
}
// se passou nos testes seta null como sucesso
setValidy(State.SUCCESS);
}, TIMEOUT_VALIDY);
return () => clearTimeout(timeoutId);
}, [value]);
const isError = typeof validy == "string";
return {
aviso: isError ? validy : null,
isLoading: validy == State.LOADING,
isValid: validy === State.SUCCESS,
};
}
}
Não coloquei as tipagens por que o código ia ficar muito grande. O arquivo completo com as tipagens está aqui
Classe que extende a anterior com componentes Tamagui:
A parte do constructor está complicada porque realmente perdi a paciência tentando deixar o camposAdd
opcional com valor padrão. Typescript é uma maravilha na zona de conforto, mas fora dela...
Pois bem, eu acabei criando essa classe extendendo a outra para não ter que ficar definindo os componentes do input e dos avisos toda vez que for usar a classe. Também coloquei validações padrão (o validsPaia) e um botão de submit como método.
type ValidKeys = keyof typeof validsPaia;
// class extendida do FormPaia com os Input e AvisoType definido
export class FormPaiado<C extends string = ValidKeys> extends FormPaia<
C,
GetProps<typeof InputPaia>
> {
constructor(
submitOptions: Options<C>["submitOptions"],
camposAdd?: Record<C, ValidacoesCampo>
) {
const campos = camposAdd ?? (validsPaia as Record<C, ValidacoesCampo>);
super({ campos, submitOptions }, InputPaia, AvisoPaia);
}
/**
* ButtonSubmit
*/
public ButtonSubmit = (props: { text: string }) => {
const { isPending } = this.useApiPaia;
return (
<ButtonCustom
disabled={isPending}
marginLeft="-2%"
mt={"$4"}
width="104%"
onPress={this.submit}
>
{isPending ? "aguarde" : props.text}
{isPending && <Spinner size="large" color={"$blue10Dark"} />}
</ButtonCustom>
);
};
}
/**
* VIEW que vai em volta do input
*/
function AvisoPaia(props: TemplateInputProps) {
const { aviso, campo, children, isValid, isLoading } = props;
const [color, texto] = (isValid && ["green", `${campo} valido`]) ||
(isLoading && ["#FFA500", "validando..."]) ||
(aviso && ["red", aviso]) || ["", " "]; // pratico, mas feio
return (
<YStack w="100%" mb={"$2"}>
<Text fontSize={"$2"} color="green" mb="$1.5" fontFamily={"$outfitBold"}>
{campo.toUpperCase()}:
</Text>
{children}
<Text fontSize={"$4"} mt={"$1"} color={color}>
{texto}
</Text>
</YStack>
);
}
/**
* Modelo padrao de input
*/
export function InputPaia(props: InputProps) {
return (
<Input
size="$4.5"
borderWidth={1.7}
borderColor="green"
backgroundColor="#e8f5e9"
color="black"
marginLeft="-2%"
width="104%"
focusStyle={{ borderColor: "green" }}
hoverStyle={{ borderColor: "green" }}
{...props}
/>
);
}
As validações ficam em um arquivo separado para melhor organização:
Estas são as validações que utilizo no FormPaiado (uma classe estendida do FormPaia). A lógica de validação do FormPaia é assíncrona e sequencial, ou seja, a próxima verificação só será executada se a verificação anterior retornar false (false significa que não houve erro/validação deu certo). Com isso, a função verifyEmail só será chamada se todas as validações anteriores (do email) retornarem false
async function verifyEmail(email: string) {
try {
const { data } = await axios.get(
`${baseURL}/usuario/verify/?email=${email}`,
{ timeout: 2000 }
);
return !data.emailDisponivel && data.message;
} catch (error) {
return true;
}
}
/**
* validações de campos pre definidas
*/
export const validsPaia = {
email: [
[(t) => t.length < 1, "Insira um email"],
[(t) => t.length > 64, "Máx. 64 caracteres"],
[(t) => !/^[a-zA-Z0-9._%+-]+/.test(t), "Caractere inválido no início"],
[(t) => !/@[a-zA-Z0-9.-]+\./.test(t), "Formato de email inválido"],
[(t) => !/[a-zA-Z]{2,}$/.test(t), "Terminação inválida"],
[verifyEmail, "Erro ao verificar o email"],
],
apelido: [
[(t) => t.length < 1, "Insira um apelido/nome"],
[(t) => t.length < 3 || t.length > 40, "Entre 3 e 40 caracteres"],
[
(t) => !/^[a-zA-Z\s\dÀ-ú]{3,40}$/.test(t),
"Apenas letras, números e espaços",
],
],
nome: [
[(t) => t.length < 1, "Insira um nome"],
[(t) => t.length < 3 || t.length > 40, "Entre 3 e 40 caracteres"],
[(t) => !/^[a-zA-Z\s\dÀ-ú]{3,40}$/.test(t), "Apenas letras e espaços"],
],
senha: [
[(t) => t.length < 1, "Insira uma senha"],
[(t) => t.length < 4 || t.length > 40, "Entre 4 e 40 caracteres"],
],
} as Record<string, ValidacoesCampo>;
Sim, me arrependi de colocar um timeout de 2s. A internet do lugar da apresentação era lenta...
Exemplo de uso
import { FormPaia } from "@/components/FormClass";
import { FormPaiado } from "@/components/FormConfigs";
import FormFooter from "@/components/FormFooter";
import TAuth from "@/components/templateAuth";
import { storeAuth } from "@/lib/logicAuth";
import { router } from "expo-router";
export default function Cadastrar() {
const login = storeAuth((s) => s.login);
const SignUp = new FormPaiado((axios) => ({
mutationFn: async (allValues) => {
await axios.post("/usuario", allValues); //se der erro n continua
const { apelido, ...credentials } = allValues;
const { data } = await axios.post("/usuario/login", credentials);
login(data.token);
router.replace("/(app)/(home)");
},
}));
return (
<TAuth subTitulo="Cadastra-se" titulo="CADASTRAR">
<SignUp.Input campo="apelido" />
<SignUp.Input campo="email" textContentType="emailAddress" />
<SignUp.Input campo="senha" secureTextEntry textContentType="password" />
<SignUp.ButtonSubmit text="CADASTRAR" />
<FormFooter
link={{
href: "/(auth)/login",
text: "Já está cadastrado?",
textLink: "Entre aqui!",
}}
/>
</TAuth>
);
}
Tela de cadastrar:
Desculpe qualquer erro de português ou na estrutura do texto, não tenho costume de escrever tanto.
LINKS: