Índice:
- Etapa 1: Configurando sua própria cópia
- Etapa 2: Visão geral do Formulário Google
- Etapa 3: Scripts do Google: (Server Code.gs) Análise inicial dos dados e do código
- Etapa 4: Habilitando OnFormSubmit
- Etapa 5: Configurando a interface do usuário
- Etapa 7: Projeto concluído
- Etapa 8: Etapa 1: o código de back-end (Server Code.gs)
- Etapa 9: Etapa 2: a parte 2 do código de back-end (Server Calls.gs)
- Etapa 10: Etapa 3: o código HTML (Application.html)
- Etapa 11: Etapa 4: o código JavaScript (JS.html)
- Etapa 12: Etapa 5: as ações de clique em código JavaScript (JS.html)
- Etapa 13: Fim … finalmente
Vídeo: Organizador do guarda-roupa: 13 etapas
2024 Autor: John Day | [email protected]. Última modificação: 2024-01-30 11:37
Quer seja para comprar roupas ou sempre que pedir emprestado qualquer item, há momentos em que você gostaria de poder espiar em seu armário de qualquer lugar para ver se você tem algo semelhante. O Organizador de Guarda-Roupa faz exatamente isso E MAIS!
Este é um balcão único e amplo para muitos outros fins. My Wardrobe Organizer é uma combinação do Google Sheets como um banco de dados SQL, Google Scripts para lidar com os dados e Google WebApp como um portal online para esses dados. O usuário final pode ver todos os itens, filtrar por algo específico, marcar itens como emprestados, gerenciar a roupa suja e impedir que a mãe compre para você a mesma camiseta no Natal todos os anos *.
(* Sem garantia. As mães comprarão o que quiserem, quer você precise ou não)
Olhando rapidamente para o design do site na imagem acima, pode-se reconhecer um layout familiar. O organizador do guarda-roupa é configurado como qualquer site de roupas comum. Dividida por departamentos na parte superior e filtros fornecidos na lateral, essa interface traz familiaridade com a funcionalidade para o usuário casual. E é simples de usar.
Etapa 1: Configurando sua própria cópia
Vamos começar criando sua própria cópia deste projeto.
Google Drive
Clique no link acima para acessar minha versão atual deste aplicativo.
Você verá três itens nesta pasta: um Formulário Google, uma Planilha Google e uma Pasta.
Clique com o botão direito na Planilha Google e clique em Fazer uma cópia.
Defina o local desta cópia para seu próprio Drive.
Depois de copiar este documento, o Formulário Google será gerado automaticamente na mesma pasta em que você moveu o Planilhas Google.
Para criar a pasta (isso é necessário para coletar uploads das imagens do item), clique no Formulário Google copiado e um prompt aparecerá solicitando que você restaure o local da pasta para uploads.
Agora você tem uma cópia deste documento para trabalhar por si mesmo!
Etapa 2: Visão geral do Formulário Google
Agora que você tem sua própria versão deste aplicativo, vamos dar uma olhada.
Seu Formulário Google está configurado para aceitar muitos tipos diferentes de itens. No entanto, camisas, calças, vestidos e sapatos têm diferentes limitações de tamanho. Portanto, uma seção diferente deste formulário será preenchida com base no departamento em que você arquiva seu item. No meu modelo (Artigo Masculino), criei 5 categorias de dimensionamento diferentes. (Para artigos femininos, clique aqui, existem muitos mais).
Em cada seção de dimensionamento, estabeleci um título exclusivo para cada parâmetro que irei coletar. Não queremos ter várias colunas em nosso banco de dados com o nome "Tamanho" ou não seríamos capazes de determinar a que tipo de roupa esse tamanho se aplica.
Ao final de cada seção, o usuário é direcionado para a parte final deste formulário: Localização. Eu pessoalmente escolhi adicionar Local para determinar os itens na Lavanderia, na lavanderia, em seu local ou aqueles itens que pedi emprestado a um amigo. Isso me permite ser organizado e nunca sentir que estou perdendo uma peça de roupa em algum lugar.
Como mencionei no início, este projeto pode ser expandido de um milhão de maneiras diferentes. Você pode usá-lo para inventário, uma ferramenta de organização mais precisa ou estritamente para pedir roupas emprestadas. Os campos e seções que você pode adicionar são infinitos, então não se sinta limitado ao que está em meu formulário. (Para artigos femininos clique aqui)
Antes de enviar alguns de seus próprios itens, vamos passar para a próxima etapa para garantir o envio adequado.
Etapa 3: Scripts do Google: (Server Code.gs) Análise inicial dos dados e do código
Ao clicar no documento do Planilhas Google, você verá muitas colunas de dados (e algumas linhas, deixadas para demonstração). Durante o envio do formulário, algumas seções são ignoradas, isso é evidente pela falta de dados em algumas colunas. Mas colunas adicionais como ID, Local padrão, Quem e Atualizado foram adicionadas para controlar melhor as edições desses itens.
Um campo de ID foi criado quando você enviou o formulário para permitir um identificador exclusivo ao percorrer este banco de dados. Para criar este campo, daremos uma olhada no Editor de Script clicando em Ferramentas> Editor de Script.
Com o Editor de scripts aberto, você notará 8 documentos na barra lateral desta nova janela. Esses documentos ajudam a controlar o processo de back-end, telas de front-end e funcionalidade de front-end. Iremos pular para cada um (se você ficar por perto), mas agora clique em Código do Servidor.
No arquivo Server Code.gs, há muitas funções:
onSubmit (e), onOpen (), doGet (), include (fileName), openApplication (), openLaundryApp (), changeValueOnSubmit (e), setIDOnSubmit (e)
onSubmit (e) - Esta função será configurada como a primeira função a ser executada quando um formulário do Google for enviado. Você pode colocar outras funções dentro desta função para permitir que muitos processos diferentes aconteçam.
onOpen (e) - Esta função é chamada quando o Google Sheets é aberto. Ele preenche uma nova opção de menu para permitir acesso rápido aos links e visualizações do aplicativo.
doGet () - Esta função é chamada na chamada de endereço do Web App. Quando um usuário navega até o aplicativo da Web publicado, esse código dirá a essa página o que exibir. Nesse caso, é o documento Application.html.
include (fileName) - Esta função é usada dentro de páginas HTML para ler outro documento e inserir seu conteúdo em um formato HTML apropriado em outra página. Nós o usamos para nossos arquivos CSS.html e JS.html.
openApplication () e openLaundryApp () - Essas funções contêm o código a ser executado quando um usuário clica nos botões de menu adicionados à barra de ferramentas do Planilhas Google.
changeValueOnSubmit (e) e setIDOnSubmit (e) - Estas são as funções que examinaremos agora. Eles são responsáveis por atualizar certos campos com valores padrão quando o formulário é enviado pela primeira vez.
Etapa 4: Habilitando OnFormSubmit
Essas duas funções, changeValueOnSubmit (e) e setIDOnSubmit (e), precisam ser conectadas à ação do usuário de enviar um formulário. Para fazer isso, precisamos habilitar um Trigger.
Habilitamos o gatilho clicando em Editar> Gatilhos do projeto atual. Isso abre o Google Developer Hub.
No canto inferior direito do painel do gatilho, há um botão Adicionar um gatilho. Clique aqui.
Agora vamos configurar a função para ser executada quando um formulário for enviado. No nosso caso, tenho várias funções (changeValueOnSubmit (e) e setIDOnSubmit (e)) que coloquei dentro de uma função onSubmit (), então só preciso configurar 1 gatilho. Portanto, selecionaremos onSubmit () e definiremos esse gatilho para ser executado no envio do formulário.
Agora temos um formulário de trabalho que preencherá uma Planilha Google com identificadores exclusivos e definirá os valores padrão.
Agora você pode fazer upload de seus próprios itens usando o Formulário Google. (Isso não é necessário para continuar, pois já existem valores de demonstração em). Agora vamos mergulhar na interface do usuário.
Etapa 5: Configurando a interface do usuário
RECEBER! Finalmente alcançamos a parte que você queria, a Interface do Usuário !!!!
À primeira vista, não há nada aqui. Ainda não fizemos nenhuma chamada. Para carregar a página mais rápido, decidi não atormentar a primeira página com TODOS os seus itens e permitir que você clique no que deseja ver mais rápido. Como este é o caso, não há itens no campo de conteúdo principal e nem filtros na barra lateral. Vamos clicar em Todos para ver o que está em nosso banco de dados.
Agora carregamos todos os itens de nosso banco de dados no campo de conteúdo principal. Você verá fotos, números de identificação, cores, tamanhos e locais. O campo de localização pode ser atualizado aqui mesmo! Se você decidir emprestar o item, você pode selecionar essa opção, você pode colocá-lo em seu armário, cômoda ou lavanderia.
E em nossa barra lateral, temos todos os campos possíveis para cada item de roupa em nossa nova consulta. Imagine ter 20 opções de tamanho diferentes nesta barra lateral, não seria muito eficaz, então vamos restringir nossa pesquisa clicando em Acessórios.
Agora que carregamos os Acessórios, dê uma olhada na barra lateral. Ele foi ajustado para apenas 3 campos, pois esses são os parâmetros que se aplicam a todos os itens desta consulta. Vou fazer uma espécie por cor. Ao clicar na cor, uma caixa suspensa aparece. Aqui, posso digitar a cor desejada e selecioná-la ou, se vir minha opção imediatamente, apenas clico nela. Selecionei vermelho para esta demonstração. Clique em Aplicar filtro na parte inferior desta barra lateral e o conteúdo principal será atualizado, mostrando os itens que têm a cor vermelha definida como parâmetro de cor.
Mencionei anteriormente que esse banco de dados me ajuda a gerenciar meus itens emprestados e na lavanderia. Para tornar um pouco mais fácil, em vez de clicar manualmente em todos os locais suspensos nesta página principal, criei o Modo de Lavanderia. Volte para a página do Planilhas Google e, em Visualização do aplicativo, você verá Modo de lavanderia. Esta opção irá puxar para cima um modal menor que mostra apenas itens com a localização da Lavanderia. Agora posso marcar todos esses itens como Padrão, o que os colocará de volta em seus locais onde normalmente estão armazenados.
Etapa 7: Projeto concluído
PARABÉNS
Para aqueles que desejam apenas um banco de dados funcional para gerenciar seus itens, sejam bem-vindos ao seu Organizador Online. Para aquelas mentes curiosas interessadas no código por trás deste aplicativo. Fique por perto enquanto eu decompô-lo.
* Você é livre para excluir os itens de teste DEPOIS de inserir pelo menos um de seus próprios itens no banco de dados. (Explicarei mais tarde se você ficar por aqui).
Etapa 8: Etapa 1: o código de back-end (Server Code.gs)
Anteriormente, abrimos o arquivo Server Code.gs e dei uma rápida descrição de cada uma das funções, já que sua finalidade era servir a cada um dos itens que você acabou de configurar, mas agora iremos analisá-los algumas das funcionalidades e utilidades chamadas para tornar este código um sucesso.
1) Tabela transversal:
var ss = SpreadsheetApp.getActiveSpreadsheet (); var sheet = ss.getSheetByName ("Respostas do formulário 1"); var range = sheet.getRange (1, 1, sheet.getMaxRows ()); var rowNum = range.getLastRow ();
- Este código é uma base para percorrer uma Planilha Google. Eu chamo a planilha pelo nome em vez do número para que, se as planilhas forem excluídas ou reorganizadas por função, ainda funcionem corretamente.
- Neste código, estou coletando apenas o intervalo de todos os dados da tabela.
2) Atribuindo um ID:
var LastID = range.getCell (rowNum-1, 1); var CellValue = Number (LastID.getValue ()); var ColA = 1; var max = 15; var min = 5; CellValue = CellValue + Math.round ((Math.random () * (max - min) + min)); e.source.getActiveSheet ().getRange (range.getLastRow (), ColA).setValue (CellValue); changeValueOnSubmit (e);
- Anteriormente, pedi que os valores de demonstração fossem deixados na tabela até que o usuário tenha enviado pelo menos um valor para si. Isso ocorre porque o gerador de ID automático depende do último valor no banco de dados para ser preenchido.
- Eu busco a última segunda à última linha porque a última linha é nosso novo valor e a primeira coluna para o valor de ID.
- Em seguida, gero aleatoriamente um número entre 5 e 15 e o adiciono ao último valor. *
- Por fim, coloco esse valor na coluna ID da última linha.
- Em seguida, chamamos a função changeValueOnSubmit (e).
* Escolhi 5-15 para permitir futura etiquetagem e integração com o Google Home para que os números não fiquem próximos o suficiente para causar confusão em cabides, etiquetas de roupas ou códigos de barras.
3) Alteração do valor do URL:
var DataChange = e.namedValues ["Imagem do item"]; var DefaultLocation = e.namedValues ["Onde você guarda esta peça de roupa?"]; var ColD = ColumnID _ ("Imagem do item") +1; var ColLoc = ColumnID _ ("Local padrão") + 1; DataChange = DataChange.toString (). replace ("abrir?", "uc? export = view &"); e.source.getActiveSheet ().getRange (e.range.rowStart, ColD).setValue (DataChange); e.source.getActiveSheet ().getRange (e.range.rowStart, ColLoc).setValue (DefaultLocation);
- Ao enviar uma foto por meio de um Formulário Google, o URL inserido no Planilhas Google é um link para o documento real. No nosso caso, como estamos criando uma página HTML, queremos que o link seja apenas a imagem.
- Ao alterar o "abrir?" parte da URL para "uc? export = view &" criamos um link para a imagem.
- Colocaremos novamente esse novo valor no local do link da imagem do item atual.
- Também estou definindo o "Local padrão" e o "Local atual" do item para a mesma coisa no banco de dados. Isso vai ajudar quando tento usar o meu Modo de Lavanderia.
-
Vamos mergulhar nisso na próxima página, mas esta é a nossa primeira visão da função ColumnID_ () que criei.
Esta função usa Nomes de Colunas para traduzi-los no número inteiro da coluna, o que é útil para chamadas de intervalo que requerem um número de coluna em vez de nome
4) SpreadsheetApp.getUI ()
- Na segunda imagem, você pode ver o uso de SpreadsheetApp.getUI () como usado para criar uma adição do Menu da Barra de Ferramentas ao Planilhas Google.
- A função.getUI () também ajuda a criar um pop-up modal que é usado para o modo Lavanderia e como um link rápido para a interface do site.
5) HTMLService
- Existem dois tipos de HTMLServices usados neste código: Template e HTMLOutput
- O modelo permite que o código seja inserido dentro do código HTML para que as informações provenientes de um servidor possam ser preenchidas quando a página for chamada.
- Saída HTML exibe páginas HTML simples.
- Também temos o método includes (), que nos permite criar vários arquivos HTML e combiná-los em um arquivo HTML modelado, retornando o conteúdo do arquivo em um formato HTML em vez de uma string.
Anexei um documento configurado como o Google App Scripts Documentation para familiarizar-se com o modo como o código-fonte e a funcionalidade são explicados no Google Apps.
Etapa 9: Etapa 2: a parte 2 do código de back-end (Server Calls.gs)
Agora entramos no Server Calls.gs. Essas funções são usadas principalmente no HTML JavaScript, portanto, foram separadas do código usado principalmente no back-end localizado em Server Code.gs.
Figura 1) Variáveis globais:
Figura 2) Buscando itens:
Figura 3) fetchItemsQry
Figura 4) filterItems
Figura 5) fetchFiltersWithQry
Figura 6) ColumnID e CacheCalls
Há muito o que falar sobre cada um deles. E para quebrar o código e explicar o que está acontecendo, eu precisava de um pouco mais de espaço de digitação. Em anexo está um documento para a divisão do código de ServerCalls.gs
Este documento é configurado como a documentação de scripts do Google App e até faz links para objetos semelhantes.
Etapa 10: Etapa 3: o código HTML (Application.html)
O código HTML fica muito infeliz dentro da caixa de diálogo do Instructable. Então, por favor, acompanhe as fotos acima.
1) No cabeçalho da página Application.html estabelecemos um título e chamamos nossa página CSS.html para ser carregada.
* Por ser uma página HTML modelada, podemos adicionar mais código a este documento sem bagunçar a tela atual usando o método de inclusão (pageName) mencionado anteriormente, encontrado em Server Code.gs
A caixa de cabeçalho principal também se encontra nesta imagem. Você pode alterar o cabeçalho aqui e inserir "Guarda-roupa de [Seu nome]" ou qualquer outra coisa como você gostaria de reconhecer esta página.
2) Logo abaixo do cabeçalho está nossa barra de navegação superior.
Esta barra de navegação inclui todos os tipos de artigos listados na folha de artigos dentro de nossas Planilhas Google.
Uma função embutida é chamada para buscar uma matriz desses itens. Em seguida, um loop é executado para incluir cada uma dessas opções como um botão de menu, completo com um código de ação para que quando um usuário clicar no botão de menu, os respectivos itens aparecerão na área do corpo.
3) O corpo principal.
Existem 4 partes para esta parte. Uma saída de texto, o filtro da barra lateral, as imagens do corpo principal e o JS inclui.
A saída de texto permite que o usuário tenha uma visão rápida do texto para os tipos de itens que estão vendo no momento, em vez de consultar a opção de menu selecionada.
O filtro da barra lateral contém muitos filtros disponíveis para o tipo de item que um usuário selecionou. Esses filtros refletem todas as opções disponíveis para esta categoria, bem como quantos itens se enquadram no valor dessa categoria. Esta barra lateral é preenchida com código JavaScript (que será discutido a seguir).
O corpo principal está vazio no momento, mas assim como os filtros, ele será preenchido com caixas de itens detalhando a ID, cor, tamanho e localização do item com uma imagem incluída quando o usuário seleciona uma categoria e o código JavaScript preenche essa área.
Finalmente, o includes (JS), vamos dar uma olhada nisso na próxima etapa.
Etapa 11: Etapa 4: o código JavaScript (JS.html)
Se você pensou que o Código do Servidor era uma seção pesada, veja só.
Aqui, combinamos nosso HTML e SeverCode com as interações do usuário. Qualquer item clicado deve ser processado aqui para obter os dados adequados e devolvê-los em um formato legível. Então, vamos dar uma olhada em nossas primeiras ligações:
O script chama: Estou usando 3 bibliotecas diferentes para este projeto; jquery, bootstrap e um add-on especial bootstrap-select. Essas bibliotecas permitem a formatação de objetos e chamadas mais fáceis para os elementos no código HTML.
Minha próxima linha importante de JavaScript está abaixo:
$ (documento).keypress (função (evento) {if (event.which == '13') {event.preventDefault (); }});
Aqui estou desabilitando a tecla enter de acionar qualquer um dos formulários. Como neste caso, os Google Web Apps recebem apenas o endereço de uma página. Pressionar enter adicionaria dados ao endereço HTML e tentaria redirecionar o usuário. Ao desabilitar isso, você permite que seu código JavaScript faça todo o trabalho.
function removeFilters () {google.script.run.withSuccessHandler (updateItems).withFailureHandler (onFailure). ServerRemoveFilters (); }
função updateDBlocation (id, value) {google.script.run.withSuccessHandler (allGood).withFailureHandler (FailDBUpdate).updateLocation (id, value); }
Aqui estão duas funções que estão fazendo chamadas para o arquivo Server Code.gs. A linha:
google.script.run.withSuccessHandler (updateItems).withFailureHandler (onFailure). ServerRemoveFilters ();
tem muitas partes funcionais, mas o esqueleto é enraizado em "google.script.run", que informa à página HTML que a função a seguir está no servidor.
- A última parte desse código é a função a ser executada. Neste exemplo ServerRemoveFilter ()
- Adicionando um withSuccessHandler (), a página HTML agora sabe o que fazer com os dados que são retornados, e isso é executar a função entre parênteses.
- O mesmo se aplica a withFailureHandler ()
Agora que analisamos a chamada do código do servidor, vamos dar uma olhada rápida no que acontece quando essas chamadas do servidor são bem-sucedidas e falham.
função allGood (e) {console.log ("Sucesso no servidor"); } function onFailure (error) {$ ("# message-box"). html ("
Não foi possível buscar itens de roupa no momento. ERRO: "+ erro.mensagem +"
");} função FailDBUpdate (erro) {$ (" # caixa de mensagem "). html ("
Você não tem acesso para modificar o local. ERRO: "+ erro.mensagem +"
"); $ (". location-selects "). prop ('disabled', 'disabled');}
Eu criei um log de console muito simples para significar sucesso quando a função de localização é executada, que você pode ver como allGood ().
Ao lidar com os erros, essas duas funções geram a mensagem de erro onde o usuário pode ver usando uma chamada jQuery para o objeto HTML com um ID de "caixa de mensagem".
Agora vamos ao trabalho árduo
Etapa 12: Etapa 5: as ações de clique em código JavaScript (JS.html)
A barra de menu superior possui opções para cada tipo de artigo. A função que eles executam no clique é:
function filterType (article, id) {$ ("ul.navbar-nav li.active"). removeClass ("active"); $ ("# currentArticle"). html ("// CÓDIGO HTML AQUI");
updateSideBar = true;
google.script.run.withSuccessHandler (updateItems).withFailureHandler (onFailure).fetchItems ("Artigos", artigo); var newSelect = "#tipo -" + id; $ (newSelect).addClass ("ativo"); $ ("# myNavbar"). removeClass ("in"); }
Podemos ver neste código que temos um google.script.run que chama o servidor para recuperar informações. A função de sucesso para esta chamada é updateItems ().
FIGURA 1 (com o código HTML pesado dentro desta função é difícil copiar estritamente o código, sem aparecer uma bagunça nesta caixa)
No código updateItems (), muitas coisas acontecem. Mais uma vez, devemos percorrer o Objeto que foi devolvido para nós e adicionar cada item à página do corpo principal.
O código HTML é adicionado como Arrays para dividir o código e tornar mais fácil de ler e ver onde itemData está sendo inserido.
No loop de cada item, estou removendo campos que não quero ver na descrição, como Padrão, carimbo de data / hora e URL da imagem. Eu removo o URL da imagem da descrição porque ele está sendo adicionado como o href a uma tag. Depois que essas informações são reunidas, elas são enviadas ao corpo principal usando a função jQuery.append ().
Após todos os itens terem sido adicionados à página, esta consulta de itens é enviada novamente para o Código do Servidor para classificar e retornar as opções de filtro como pode ser visto na Figura 2.
FIGURA 2 (atualizando o SideBar)
Muito semelhante à função updateItems (), mais uma vez temos matrizes de código HTML e um loop para todas as opções de filtro. A única mudança perceptível é o jQuery.selectpicker ('atualizar'). Esta função vem da biblioteca de scripts que incluímos na última etapa. Ele permite que o programador escreva um HTML de seleção simples e deixe a biblioteca atualizá-lo para incluir a função pesquisável, bem como o código CSS.
IMAGEM 3 (filtrando com a barra lateral)
Por último, temos a função updateFilter (formData). Isso é usado quando um formulário é enviado a partir da barra lateral. Começamos usando uma função jQuery.serializeArray () que lê o código HTML do elemento definido em nosso caso um formulário e retorna os valores em uma string a ser enviada ao servidor. E recomeçamos o processo a partir da Figura 1.
Etapa 13: Fim … finalmente
Bem, aí está; uma explicação completa e completa para ajudá-lo a configurar seu próprio guarda-roupa online ou utilizar a funcionalidade criada em meus scripts do Google para expandir seu próprio projeto.
Foi uma jornada codificar este projeto (e documentá-lo por meio deste Instructable), mas gostei do processo e espero que você goste do produto. Eu adoraria ouvir a resposta de qualquer pessoa que faça ajustes quando Michael Jordan diz "O teto é o telhado" e eu concordo que este aplicativo não tem limites.
Recomendado:
Rainbo Skyz, um guarda-chuva LED hackeável: 9 etapas (com fotos)
Rainbo Skyz, um guarda-chuva de LED que pode ser hackeado: Faça seu próprio guarda-chuva de LED
Luz do guarda-chuva: 9 etapas (com fotos)
Luz do guarda-chuva: Já voltou para casa em uma noite escura e chuvosa e um carro ou ciclista quase bateu em você? * Wooosh! * Ciclistas traquinas. Como ele não me viu lá?! Um cruzamento depois … * vrrrooooomm! * Motoristas traquinas! Eu estava quase arrasado! Depois de um dia difícil em w
Torre de guarda versus insetos: 12 etapas
Torre de Guarda Versus Insetos: Somos alunos do primeiro ano do Instituto Conjunto UM-SJTU, localizado no campus Ming Hang da Universidade Jiaotong de Xangai, Xangai, China. Estamos aqui para formar o Grupo 13 para a Introdução à Engenharia VG100 curso do JI, e
Guarda-chuva elétrico: 7 etapas (com fotos)
Guarda-chuva elétrico: Transforme um guarda-chuva comum em algo caprichoso e mágico. O guarda-chuva elétrico brilhará com muitos pontos de luz. Leve o sol e as estrelas com você à noite! Perfeito para passeios noturnos pelo campo ou apenas para relaxar
Guarda-sol para laptop faça você mesmo: 5 etapas
Guarda-sol para laptop DIY: Faça seu próprio guarda-sol para laptop usando sacolas de papel (3 no máximo), uma tesoura, fita adesiva e uma caneta / lápis. Divirta-se fazendo! howgreenis.blogspot.com