diff --git a/.gitignore b/.gitignore index 85e7c1d..5ef28de 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /.idea/ +.DS_Store + diff --git a/README.md b/README.md index c9860aa..11d5617 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # [YourSmartHouse](https://gdonisi.github.io/YourSmartHouse) -Demo of my University Project for "Software Technologies for the Web" class. Original source can be found [here](https://github.com/giovannidonisi/YourSmartHouse). +Demo of my University Project for "Software Technologies for the Web" class. diff --git a/YourSmartHouse.war b/YourSmartHouse.war new file mode 100644 index 0000000..8429ac6 Binary files /dev/null and b/YourSmartHouse.war differ diff --git a/YourSmartHouse/.gitattributes b/YourSmartHouse/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/YourSmartHouse/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/YourSmartHouse/YourSmartHouse.iml b/YourSmartHouse/YourSmartHouse.iml new file mode 100644 index 0000000..300deec --- /dev/null +++ b/YourSmartHouse/YourSmartHouse.iml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YourSmartHouse/pom.xml b/YourSmartHouse/pom.xml new file mode 100644 index 0000000..15cd18b --- /dev/null +++ b/YourSmartHouse/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + GiovanniDonisi + YourSmartHouse + 1.0-SNAPSHOT + YourSmartHouse + war + + + 1.8 + 1.8 + 5.7.1 + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/java/carrello/Carrello.java b/YourSmartHouse/src/main/java/carrello/Carrello.java new file mode 100644 index 0000000..526e3c4 --- /dev/null +++ b/YourSmartHouse/src/main/java/carrello/Carrello.java @@ -0,0 +1,66 @@ +package carrello; + +import prodotto.Prodotto; + +import java.util.ArrayList; +import java.util.Optional; + +public class Carrello { + private ArrayList oggetti; + + public Carrello() { + super(); + this.oggetti = new ArrayList<>(); + } + + public Carrello(ArrayList o){ + this.oggetti = o; + } + + public boolean addProduct(Prodotto p, int q) { + Optional opt = find(p.getProductId()); + if(opt.isPresent()) { + opt.get().setQuantita(q); + return true; + } + else + return oggetti.add(new OggettoCarrello(p, q)); + } + + public boolean removeProduct(int id) { + return oggetti.removeIf(it -> it.getProdotto().getProductId() == id); + } + + public Optional find(int id) { + return oggetti.stream().filter(it -> it.getProdotto().getProductId() == id).findFirst(); + } + + public void reset() { + oggetti.clear(); + } + + public ArrayList getOggetti() { + return oggetti; + } + + public void setOggetti(ArrayList oggetti) { + this.oggetti = oggetti; + } + + public double getTotale() { + double x = 0.0; + for(OggettoCarrello o : oggetti){ + x += o.totale(); + } + x = Math.round(x * 100); + return x/100; // Mi assicuro che i double abbiano 2 cifre dopo la virgola + } + + public int getQuantita() { + int a = 0; + for(OggettoCarrello o : oggetti) { + a += o.getQuantita(); + } + return a; + } +} diff --git a/YourSmartHouse/src/main/java/carrello/CartServlet.java b/YourSmartHouse/src/main/java/carrello/CartServlet.java new file mode 100644 index 0000000..55b4c93 --- /dev/null +++ b/YourSmartHouse/src/main/java/carrello/CartServlet.java @@ -0,0 +1,99 @@ +package carrello; + +import http.Controller; +import http.InvalidRequestException; +import prodotto.Prodotto; +import prodotto.ProdottoDAO; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.sql.SQLException; + +@WebServlet(name = "CartServlet", value = "/cart/*") +public class CartServlet extends Controller { + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = getPath(req); + switch (path) { + case "/": + case "/show": + Carrello c = getSessionCart(req.getSession(false)); + if(c == null) { + c = new Carrello(); + req.getSession(false).setAttribute("utenteCarrello", c); + } + req.getRequestDispatcher(view("utente/carrello")).forward(req, resp); + break; + default: + resp.sendError(404); + } + } + + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + String path = getPath(req); + switch (path) { + case "/add": + add(req, resp); + break; + case "/remove": + remove(req, resp); + break; + case "/empty": + Carrello c = getSessionCart(req.getSession(false)); + if(c != null) + c.reset(); + resp.sendRedirect("/YourSmartHouse/cart/show"); + break; + default: + resp.sendError(405); + break; + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } + } + + private void add(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + String quant = req.getParameter("quantita"); + if (!isNumber(id) || !isNumber(quant)) + throw new InvalidRequestException(500); + + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = dao.fetchProduct(Integer.parseInt(id)); + if(p != null) { + int quantita = Integer.parseInt(quant); + if(getSessionCart(req.getSession(false)) == null) + req.getSession(false).setAttribute("utenteCarrello", new Carrello()); + + getSessionCart(req.getSession(false)).addProduct(p, quantita); + resp.sendRedirect("/YourSmartHouse/products/show?id="+id); + } else + throw new InvalidRequestException(404); + } catch (SQLException | IOException throwables) { + throwables.printStackTrace(); + throw new InvalidRequestException(500); + } + } + + private void remove(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if (!isNumber(id)) + throw new InvalidRequestException(500); + + if(getSessionCart(req.getSession(false)).removeProduct(Integer.parseInt(id))) + resp.sendRedirect("/YourSmartHouse/cart/show"); + else + throw new InvalidRequestException(404); + } catch (IOException e) { + throw new InvalidRequestException(500); + } + } + +} diff --git a/YourSmartHouse/src/main/java/carrello/OggettoCarrello.java b/YourSmartHouse/src/main/java/carrello/OggettoCarrello.java new file mode 100644 index 0000000..f7ed1a6 --- /dev/null +++ b/YourSmartHouse/src/main/java/carrello/OggettoCarrello.java @@ -0,0 +1,31 @@ +package carrello; + +import prodotto.Prodotto; + +public class OggettoCarrello { + private final Prodotto prodotto; + private int quantita; + + public OggettoCarrello(Prodotto prodotto, int quantita) { + this.prodotto = prodotto; + this.quantita = quantita; + } + + public void setQuantita(int quantita) { + this.quantita = quantita; + } + + public Prodotto getProdotto() { + return prodotto; + } + + public int getQuantita() { + return quantita; + } + + public double totale() { + double n = prodotto.getPrezzo()*quantita; + n = Math.round(n * 100); + return n/100; // Mi assicuro che i double abbiano 2 cifre dopo la virgola + } +} diff --git a/YourSmartHouse/src/main/java/categoria/Categoria.java b/YourSmartHouse/src/main/java/categoria/Categoria.java new file mode 100644 index 0000000..64ad601 --- /dev/null +++ b/YourSmartHouse/src/main/java/categoria/Categoria.java @@ -0,0 +1,69 @@ +package categoria; + +import org.json.JSONObject; +import prodotto.Prodotto; + +import javax.servlet.http.Part; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.ArrayList; + +public class Categoria { + private int categoryId; + private String nome, foto; + private ArrayList prodotti; + + public Categoria() { + super(); + } + + public int getCategoryId() { + return categoryId; + } + + public void setCategoryId(int categoryId) { + this.categoryId = categoryId; + } + + public String getNome() { + return nome; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public ArrayList getProdotti() { + return prodotti; + } + + public void setProdotti(ArrayList prodotti) { + this.prodotti = prodotti; + } + + public String getFoto() { + return foto; + } + + public void setFoto(String foto) { + this.foto = foto; + } + + public void writeFoto(String up, Part st) throws IOException { + try(InputStream filestream = st.getInputStream()) { + File file = new File(up+foto); + Files.copy(filestream, file.toPath()); + } + } + + public JSONObject toJSON() { + JSONObject object = new JSONObject(); + object.put("id", categoryId); + object.put("nome", nome); + object.put("foto", foto); + return object; + } + +} diff --git a/YourSmartHouse/src/main/java/categoria/CategoriaDAO.java b/YourSmartHouse/src/main/java/categoria/CategoriaDAO.java new file mode 100644 index 0000000..1a295b4 --- /dev/null +++ b/YourSmartHouse/src/main/java/categoria/CategoriaDAO.java @@ -0,0 +1,134 @@ +package categoria; + +import http.ConPool; +import http.Paginator; +import prodotto.Prodotto; + +import java.sql.*; +import java.util.ArrayList; + +public class CategoriaDAO { + + public ArrayList fetchCategories(Paginator p) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM categoria LIMIT ?,?"); + ps.setInt(1, p.getOffset()); + ps.setInt(2, p.getLimit()); + ResultSet rs = ps.executeQuery(); + ArrayList categorie = new ArrayList<>(); + while (rs.next()) { + Categoria c = new Categoria(); + c.setCategoryId(rs.getInt("categoryId")); + c.setNome(rs.getString("nome")); + c.setFoto(rs.getString("foto")); + categorie.add(c); + } + rs.close(); + return categorie; + } + } + + public ArrayList fetchCategories() throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM categoria"); + ResultSet rs = ps.executeQuery(); + ArrayList categorie = new ArrayList<>(); + while (rs.next()) { + Categoria c = new Categoria(); + c.setCategoryId(rs.getInt("categoryId")); + c.setNome(rs.getString("nome")); + c.setFoto(rs.getString("foto")); + categorie.add(c); + } + rs.close(); + return categorie; + } + } + + public Categoria fetchCategory(int id) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM categoria WHERE categoryId=?"); + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + Categoria c = new Categoria(); + if(rs.next()) { + c.setCategoryId(rs.getInt("categoryId")); + c.setNome(rs.getString("nome")); + c.setFoto(rs.getString("foto")); + } + rs.close(); + return c; + } + } + + public boolean createCategory(Categoria c) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("INSERT INTO categoria (nome, foto) VALUE (?,?)", Statement.RETURN_GENERATED_KEYS); + ps.setString(1, c.getNome()); + ps.setString(2, c.getFoto()); + return (ps.executeUpdate()==1); + } + } + + public boolean updateCategory(Categoria c) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("UPDATE categoria SET nome=?, foto=? WHERE categoryId=?"); + ps.setInt(3, c.getCategoryId()); + ps.setString(1, c.getNome()); + ps.setString(2, c.getFoto()); + return (ps.executeUpdate()==1); + } + } + + public Categoria fetchCategoryWithProducts(int catId, Paginator paginator) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM categoria as cat INNER JOIN prodotto pro ON cat.categoryId=pro.category_fk WHERE cat.categoryId=? LIMIT ?,?"); + ps.setInt(1, catId); + ps.setInt(2, paginator.getOffset()); + ps.setInt(3, paginator.getLimit()); + ResultSet rs = ps.executeQuery(); + if(rs.next()) { + Categoria c = new Categoria(); + c.setCategoryId(rs.getInt("categoryId")); + c.setNome(rs.getString("cat.nome")); + c.setFoto(rs.getString("cat.foto")); + ArrayList prodotti = new ArrayList<>(); + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("pro.nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("pro.foto")); + p.setPrezzo(rs.getDouble("prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + prodotti.add(p); + while(rs.next()) { + Prodotto pp = new Prodotto(); + pp.setProductId(rs.getInt("productId")); + pp.setNome(rs.getString("pro.nome")); + pp.setDescrizione(rs.getString("descrizione")); + pp.setFoto(rs.getString("pro.foto")); + pp.setPrezzo(rs.getDouble("prezzo")); + pp.setDisponibilita(rs.getInt("disponibilita")); + pp.setCategoryId(rs.getInt("category_fk")); + prodotti.add(pp); + } + c.setProdotti(prodotti); + return c; + } + return null; + } + } + + public int countAll() throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM categoria"); + ResultSet rs = ps.executeQuery(); + int i = 0; + if(rs.next()) + i = rs.getInt(1); + return i; + } + } + +} diff --git a/YourSmartHouse/src/main/java/categoria/CategoryServlet.java b/YourSmartHouse/src/main/java/categoria/CategoryServlet.java new file mode 100644 index 0000000..285d370 --- /dev/null +++ b/YourSmartHouse/src/main/java/categoria/CategoryServlet.java @@ -0,0 +1,108 @@ +package categoria; + +import http.*; +import org.json.JSONArray; +import org.json.JSONObject; +import prodotto.Prodotto; +import prodotto.ProdottoDAO; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; + +@WebServlet(name = "CategoryServlet", value = "/categories/*") +public class CategoryServlet extends Controller { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + req.setCharacterEncoding("UTF-8"); + String path = getPath(req); + switch (path) { + case "/": + showAll(req, resp); + break; + case "/showAll": + showAll(req, resp); + break; + case "/show": + show(req, resp); + break; + case "/api": + if(isAJAX(req)) { + CategoriaDAO cd = new CategoriaDAO(); + ArrayList categorie = cd.fetchCategories(); + JSONObject obj = new JSONObject(); + JSONArray arr = new JSONArray(); + obj.put("categories", arr); + categorie.forEach(c -> arr.put(c.toJSON())); + sendJSON(resp, obj); + break; + } + default: + resp.sendError(404); + break; + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } catch (SQLException s) { + resp.sendError(500); + } + } + + private void show(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + int cId = Integer.parseInt(id); + + CategoriaDAO dao = new CategoriaDAO(); + Categoria c = dao.fetchCategory(cId); + if(cId > dao.countAll()) + throw new InvalidRequestException(400); + + int page = 1; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + + Paginator pagi = new Paginator(page, 12); + ProdottoDAO pdao = new ProdottoDAO(); + ArrayList prodotti = pdao.fetchProductsByCategory(cId, pagi); + int pages = pagi.getPages(pdao.countAllByCategory(cId)); + + req.getSession(true).setAttribute("cat", c); + req.getSession(true).setAttribute("prodotti", prodotti); + req.getSession(true).setAttribute("pages", pages); + req.getRequestDispatcher(view("utente/categoria")).forward(req, resp); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void showAll(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvalidRequestException { + try { + int page = 1; + String p = req.getParameter("page"); + if (p != null && !p.equals("")) + page = Integer.parseInt(req.getParameter("page")); + Paginator paginator = new Paginator(page, 10); + + CategoriaDAO dao = new CategoriaDAO(); + ArrayList categorie = dao.fetchCategories(paginator); + int pages = paginator.getPages(dao.countAll()); + + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("categorie", categorie); + req.getRequestDispatcher(view("utente/categorie")).forward(req, resp); + } catch (SQLException e) { + throw new InvalidRequestException(500); + } + } + +} diff --git a/YourSmartHouse/src/main/java/http/Alert.java b/YourSmartHouse/src/main/java/http/Alert.java new file mode 100644 index 0000000..3e89647 --- /dev/null +++ b/YourSmartHouse/src/main/java/http/Alert.java @@ -0,0 +1,23 @@ +package http; + +import java.util.ArrayList; + +public class Alert { + + private final String type; + private final ArrayList messages; + + public Alert(ArrayList messages, String type) { + this.type = type; + this.messages = messages; + } + + public String getType() { + return type; + } + + public ArrayList getMessages() { + return messages; + } + +} diff --git a/YourSmartHouse/src/main/java/http/ConPool.java b/YourSmartHouse/src/main/java/http/ConPool.java new file mode 100644 index 0000000..1125bf4 --- /dev/null +++ b/YourSmartHouse/src/main/java/http/ConPool.java @@ -0,0 +1,72 @@ +/* +BSD 3-Clause License + +Copyright (c) 2019, Mattia De Rosa +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +package http; + +/** + * @author Mattia De Rosa + * + */ + +import org.apache.tomcat.jdbc.pool.DataSource; +import org.apache.tomcat.jdbc.pool.PoolProperties; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.TimeZone; + +public class ConPool { + private static DataSource datasource; + + public static Connection getConnection() throws SQLException { + if (datasource == null) { + PoolProperties p = new PoolProperties(); + p.setUrl("jdbc:mysql://localhost:3306/yoursmarthouse?serverTimezone=" + TimeZone.getDefault().getID()); + p.setDriverClassName("com.mysql.cj.jdbc.Driver"); + p.setUsername("root"); + p.setPassword("studentiTSW"); + p.setMaxActive(100); + p.setInitialSize(10); + p.setMinIdle(10); + p.setRemoveAbandonedTimeout(60); + p.setRemoveAbandoned(true); + datasource = new DataSource(); + datasource.setPoolProperties(p); + } + return datasource.getConnection(); + } + + public static void deleteConnection() { + if(datasource != null) + datasource.close(); + } + +} diff --git a/YourSmartHouse/src/main/java/http/Controller.java b/YourSmartHouse/src/main/java/http/Controller.java new file mode 100644 index 0000000..1abb5ed --- /dev/null +++ b/YourSmartHouse/src/main/java/http/Controller.java @@ -0,0 +1,78 @@ +package http; + +import carrello.Carrello; +import org.json.JSONObject; +import utente.UtenteSession; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; + +public abstract class Controller extends HttpServlet { + + private static final String basePath = "/WEB-INF/views/"; + + protected static String getPath(HttpServletRequest req) { + String path = req.getPathInfo(); + if(path == null) + path = "/"; + return path; + } + + protected static String view(String viewPath) { // La path deve essere da views in poi + return basePath + viewPath + ".jsp"; + } + + + protected String getUploadPath() { + return System.getenv("CATALINA_HOME") + File.separator + "uploads" + File.separator; + } + + protected void authorize(HttpSession session) throws InvalidRequestException { + authenticate(session); + UtenteSession s = (UtenteSession) session.getAttribute("utenteSession"); + if(!s.isAdmin()) + throw new InvalidRequestException(403); + } + + protected void authenticate(HttpSession session) throws InvalidRequestException { + if(session==null || session.getAttribute("utenteSession")==null) { + throw new InvalidRequestException(401); + } + } + + protected void validate(RequestValidator validator) throws InvalidRequestException { + if(validator.hasErrors()) + throw new InvalidRequestException(validator.getErrors(), 400); + } + + protected boolean isAdmin(HttpSession session) { + UtenteSession s = (UtenteSession) session.getAttribute("utenteSession"); + return s.isAdmin(); + } + + protected boolean isNumber(String s) { + return (s != null && !s.isBlank() && s.matches("\\d+")); + } + + protected boolean isAJAX(HttpServletRequest req) { + return "XMLHttpRequest".equals(req.getHeader("X-Requested-With")); + } + + protected void sendJSON(HttpServletResponse resp, JSONObject obj) throws IOException { + resp.setContentType("application/json"); + resp.setCharacterEncoding("UTF-8"); + PrintWriter w = resp.getWriter(); + w.println(obj.toString()); + w.flush(); + } + + protected Carrello getSessionCart(HttpSession session) { + return (Carrello) session.getAttribute("utenteCarrello"); + } + +} diff --git a/YourSmartHouse/src/main/java/http/FileServlet.java b/YourSmartHouse/src/main/java/http/FileServlet.java new file mode 100644 index 0000000..3d840a7 --- /dev/null +++ b/YourSmartHouse/src/main/java/http/FileServlet.java @@ -0,0 +1,473 @@ +package http; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.zip.GZIPOutputStream; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet(name = "FileServlet", urlPatterns = "/photos/*") +public class FileServlet extends HttpServlet { + + // Constants ---------------------------------------------------------------------------------- + + private static final int DEFAULT_BUFFER_SIZE = 10240; // ..bytes = 10KB. + private static final long DEFAULT_EXPIRE_TIME = 604800000L; // ..ms = 1 week. + private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES"; + + // Properties --------------------------------------------------------------------------------- + + private String basePath; + + // Actions ------------------------------------------------------------------------------------ + + /** + * Initialize the servlet. + * + * @see HttpServlet#init(). + */ + @Override + public void init() throws ServletException { + + this.basePath = System.getenv("CATALINA_HOME") + File.separator + "uploads"; + + // Validate base path. + if (this.basePath == null) { + throw new ServletException("FileServlet init param 'basePath' is required."); + } else { + File path = new File(this.basePath); + if (!path.exists()) { + throw new ServletException("FileServlet init param 'basePath' value '" + + this.basePath + "' does actually not exist in file system."); + } else if (!path.isDirectory()) { + throw new ServletException("FileServlet init param 'basePath' value '" + + this.basePath + "' is actually not a directory in file system."); + } else if (!path.canRead()) { + throw new ServletException("FileServlet init param 'basePath' value '" + + this.basePath + "' is actually not readable in file system."); + } + } + } + + /** + * Process HEAD request. This returns the same headers as GET request, but without content. + * + * @see HttpServlet#doHead(HttpServletRequest, HttpServletResponse). + */ + @Override + protected void doHead(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + // Process request without content. + processRequest(request, response, false); + } + + /** + * Process GET request. + * + * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse). + */ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + // Process request with content. + processRequest(request, response, true); + } + + /** + * Process the actual request. + * + * @param request The request to be processed. + * @param response The response to be created. + * @param content Whether the request body should be written (GET) or not (HEAD). + * @throws IOException If something fails at I/O level. + */ + private void processRequest + (HttpServletRequest request, HttpServletResponse response, boolean content) + throws IOException { + // Validate the requested file ------------------------------------------------------------ + + // Get requested file by path info. + String requestedFile = request.getPathInfo(); + + // Check if file is actually supplied to the request URL. + if (requestedFile == null) { + // Do your thing if the file is not supplied to the request URL. + // Throw an exception, or send 404, or show default/warning page, or just ignore it. + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + + // URL-decode the file name (might contain spaces and on) and prepare file object. + File file = new File(basePath, URLDecoder.decode(requestedFile, StandardCharsets.UTF_8)); + + // Check if file actually exists in filesystem. + if (!file.exists()) { + // Do your thing if the file appears to be non-existing. + // Throw an exception, or send 404, or show default/warning page, or just ignore it. + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + + // Prepare some variables. The ETag is an unique identifier of the file. + String fileName = file.getName(); + long length = file.length(); + long lastModified = file.lastModified(); + String eTag = fileName + "_" + length + "_" + lastModified; + long expires = System.currentTimeMillis() + DEFAULT_EXPIRE_TIME; + + + // Validate request headers for caching --------------------------------------------------- + + // If-None-Match header should contain "*" or ETag. If so, then return 304. + String ifNoneMatch = request.getHeader("If-None-Match"); + if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) { + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setHeader("ETag", eTag); // Required in 304. + response.setDateHeader("Expires", expires); // Postpone cache with 1 week. + return; + } + + // If-Modified-Since header should be greater than LastModified. If so, then return 304. + // This header is ignored if any If-None-Match header is specified. + long ifModifiedSince = request.getDateHeader("If-Modified-Since"); + if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) { + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setHeader("ETag", eTag); // Required in 304. + response.setDateHeader("Expires", expires); // Postpone cache with 1 week. + return; + } + + + // Validate request headers for resume ---------------------------------------------------- + + // If-Match header should contain "*" or ETag. If not, then return 412. + String ifMatch = request.getHeader("If-Match"); + if (ifMatch != null && !matches(ifMatch, eTag)) { + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return; + } + + // If-Unmodified-Since header should be greater than LastModified. If not, then return 412. + long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since"); + if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) { + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return; + } + + + // Validate and process range ------------------------------------------------------------- + + // Prepare some variables. The full Range represents the complete file. + Range full = new Range(0, length - 1, length); + List ranges = new ArrayList(); + + // Validate and process Range and If-Range headers. + String range = request.getHeader("Range"); + if (range != null) { + + // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416. + if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) { + response.setHeader("Content-Range", "bytes */" + length); // Required in 416. + response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return; + } + + // If-Range header should either match ETag or be greater then LastModified. If not, + // then return full file. + String ifRange = request.getHeader("If-Range"); + if (ifRange != null && !ifRange.equals(eTag)) { + try { + long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid. + if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) { + ranges.add(full); + } + } catch (IllegalArgumentException ignore) { + ranges.add(full); + } + } + + // If any valid If-Range header, then process each part of byte range. + if (ranges.isEmpty()) { + for (String part : range.substring(6).split(",")) { + // Assuming a file with length of 100, the following examples returns bytes at: + // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100). + long start = sublong(part, 0, part.indexOf("-")); + long end = sublong(part, part.indexOf("-") + 1, part.length()); + + if (start == -1) { + start = length - end; + end = length - 1; + } else if (end == -1 || end > length - 1) { + end = length - 1; + } + + // Check if Range is syntactically valid. If not, then return 416. + if (start > end) { + response.setHeader("Content-Range", "bytes */" + length); // Required in 416. + response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return; + } + + // Add range. + ranges.add(new Range(start, end, length)); + } + } + } + + + // Prepare and initialize response -------------------------------------------------------- + + // Get content type by file name and set default GZIP support and content disposition. + String contentType = getServletContext().getMimeType(fileName); + boolean acceptsGzip = false; + String disposition = "inline"; + + // If content type is unknown, then set the default value. + // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp + // To add new content types, add new mime-mapping entry in web.xml. + if (contentType == null) { + contentType = "application/octet-stream"; + } + + // If content type is text, then determine whether GZIP content encoding is supported by + // the browser and expand content type with the one and right character encoding. + if (contentType.startsWith("text")) { + String acceptEncoding = request.getHeader("Accept-Encoding"); + acceptsGzip = acceptEncoding != null && accepts(acceptEncoding, "gzip"); + contentType += ";charset=UTF-8"; + } + + // Else, expect for images, determine content disposition. If content type is supported by + // the browser, then set to inline, else attachment which will pop a 'save as' dialogue. + else if (!contentType.startsWith("image")) { + String accept = request.getHeader("Accept"); + disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment"; + } + + // Initialize response. + response.reset(); + response.setBufferSize(DEFAULT_BUFFER_SIZE); + response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("ETag", eTag); + response.setDateHeader("Last-Modified", lastModified); + response.setDateHeader("Expires", expires); + + + // Send requested file (part(s)) to client ------------------------------------------------ + + // Prepare streams. + RandomAccessFile input = null; + OutputStream output = null; + + try { + // Open streams. + input = new RandomAccessFile(file, "r"); + output = response.getOutputStream(); + + if (ranges.isEmpty() || ranges.get(0) == full) { + + // Return full file. + Range r = full; + response.setContentType(contentType); + + if (content) { + if (acceptsGzip) { + // The browser accepts GZIP, so GZIP the content. + response.setHeader("Content-Encoding", "gzip"); + output = new GZIPOutputStream(output, DEFAULT_BUFFER_SIZE); + } else { + // Content length is not directly predictable in case of GZIP. + // So only add it if there is no means of GZIP, else browser will hang. + response.setHeader("Content-Length", String.valueOf(r.length)); + } + + // Copy full range. + copy(input, output, r.start, r.length); + } + + } else if (ranges.size() == 1) { + + // Return single part of file. + Range r = ranges.get(0); + response.setContentType(contentType); + response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); + response.setHeader("Content-Length", String.valueOf(r.length)); + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. + + if (content) { + // Copy single part range. + copy(input, output, r.start, r.length); + } + + } else { + + // Return multiple parts of file. + response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY); + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. + + if (content) { + // Cast back to ServletOutputStream to get the easy println methods. + ServletOutputStream sos = (ServletOutputStream) output; + + // Copy multi part range. + for (Range r : ranges) { + // Add multipart boundary and header fields for every range. + sos.println(); + sos.println("--" + MULTIPART_BOUNDARY); + sos.println("Content-Type: " + contentType); + sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total); + + // Copy single part range of multi part range. + copy(input, output, r.start, r.length); + } + + // End with multipart boundary. + sos.println(); + sos.println("--" + MULTIPART_BOUNDARY + "--"); + } + } + } finally { + // Gently close streams. + close(output); + close(input); + } + } + + // Helpers (can be refactored to public utility class) ---------------------------------------- + + /** + * Returns true if the given accept header accepts the given value. + * + * @param acceptHeader The accept header. + * @param toAccept The value to be accepted. + * @return True if the given accept header accepts the given value. + */ + private static boolean accepts(String acceptHeader, String toAccept) { + String[] acceptValues = acceptHeader.split("\\s*(,|;)\\s*"); + Arrays.sort(acceptValues); + return Arrays.binarySearch(acceptValues, toAccept) > -1 + || Arrays.binarySearch(acceptValues, toAccept.replaceAll("/.*$", "/*")) > -1 + || Arrays.binarySearch(acceptValues, "*/*") > -1; + } + + /** + * Returns true if the given match header matches the given value. + * + * @param matchHeader The match header. + * @param toMatch The value to be matched. + * @return True if the given match header matches the given value. + */ + private static boolean matches(String matchHeader, String toMatch) { + String[] matchValues = matchHeader.split("\\s*,\\s*"); + Arrays.sort(matchValues); + return Arrays.binarySearch(matchValues, toMatch) > -1 + || Arrays.binarySearch(matchValues, "*") > -1; + } + + /** + * Returns a substring of the given string value from the given begin index to the given end + * index as a long. If the substring is empty, then -1 will be returned + * + * @param value The string value to return a substring as long for. + * @param beginIndex The begin index of the substring to be returned as long. + * @param endIndex The end index of the substring to be returned as long. + * @return A substring of the given string value as long or -1 if substring is empty. + */ + private static long sublong(String value, int beginIndex, int endIndex) { + String substring = value.substring(beginIndex, endIndex); + return (substring.length() > 0) ? Long.parseLong(substring) : -1; + } + + /** + * Copy the given byte range of the given input to the given output. + * + * @param input The input to copy the given range to the given output for. + * @param output The output to copy the given range from the given input for. + * @param start Start of the byte range. + * @param length Length of the byte range. + * @throws IOException If something fails at I/O level. + */ + private static void copy(RandomAccessFile input, OutputStream output, long start, long length) + throws IOException { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + int read; + + if (input.length() == length) { + // Write full range. + while ((read = input.read(buffer)) > 0) { + output.write(buffer, 0, read); + } + } else { + // Write partial range. + input.seek(start); + long toRead = length; + + while ((read = input.read(buffer)) > 0) { + if ((toRead -= read) > 0) { + output.write(buffer, 0, read); + } else { + output.write(buffer, 0, (int) toRead + read); + break; + } + } + } + } + + /** + * Close the given resource. + * + * @param resource The resource to be closed. + */ + private static void close(Closeable resource) { + if (resource != null) { + try { + resource.close(); + } catch (IOException ignore) { + // Ignore IOException. If you want to handle this anyway, it might be useful to know + // that this will generally only be thrown when the client aborted the request. + } + } + } + + // Inner classes ------------------------------------------------------------------------------ + + /** + * This class represents a byte range. + */ + protected class Range { + long start; + long end; + long length; + long total; + + /** + * Construct a byte range. + * + * @param start Start of the byte range. + * @param end End of the byte range. + * @param total Total length of the byte source. + */ + public Range(long start, long end, long total) { + this.start = start; + this.end = end; + this.length = end - start + 1; + this.total = total; + } + + } + +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/java/http/InvalidRequestException.java b/YourSmartHouse/src/main/java/http/InvalidRequestException.java new file mode 100644 index 0000000..8c5c4fd --- /dev/null +++ b/YourSmartHouse/src/main/java/http/InvalidRequestException.java @@ -0,0 +1,44 @@ +package http; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; + +public class InvalidRequestException extends Exception { + + private final ArrayList errors; + private final int errorCode; + + public InvalidRequestException(ArrayList errors, int errorCode) { + super(); + this.errors = errors; + this.errorCode = errorCode; + } + + public InvalidRequestException(int errorCode) { + super(); + this.errors = new ArrayList<>(); + this.errorCode = errorCode; + } + + public void handle(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { + if (errorCode == 400 && !errors.isEmpty()) { + req.setAttribute("alert", new Alert(errors, "danger")); + String backPath = (String) req.getAttribute("back"); + req.getRequestDispatcher(backPath).forward(req, resp); + } + else + resp.sendError(errorCode); + } + + public ArrayList getErrors() { + return errors; + } + + public int getErrorCode() { + return errorCode; + } + +} diff --git a/YourSmartHouse/src/main/java/http/Paginator.java b/YourSmartHouse/src/main/java/http/Paginator.java new file mode 100644 index 0000000..28a232b --- /dev/null +++ b/YourSmartHouse/src/main/java/http/Paginator.java @@ -0,0 +1,30 @@ +package http; + +public class Paginator { + + private final int limit, offset; + + public Paginator(int page, int itemsPerPage) throws InvalidRequestException { + if(page < 1 || itemsPerPage < 1) + throw new InvalidRequestException(500); + + this.limit = itemsPerPage; + if(page == 1) + this.offset = 0; + else + this.offset = (page - 1) * itemsPerPage; + } + + public int getLimit() { + return limit; + } + + public int getOffset() { + return offset; + } + + public int getPages(int size) { + int ap = (size%limit==0) ? 0 : 1; + return (size/limit)+ap; + } +} diff --git a/YourSmartHouse/src/main/java/http/RequestValidator.java b/YourSmartHouse/src/main/java/http/RequestValidator.java new file mode 100644 index 0000000..219018c --- /dev/null +++ b/YourSmartHouse/src/main/java/http/RequestValidator.java @@ -0,0 +1,69 @@ +package http; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.Pattern; + +public class RequestValidator { + + private final ArrayList errors; + private final HttpServletRequest request; + private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$"); + private static final Pattern DOUBLE_PATTERN = Pattern.compile("^(-)?(0|[1-9](\\d+)?+)(\\.\\d(\\d)?)?+$"); + + public RequestValidator(HttpServletRequest request) { + this.request = request; + this.errors = new ArrayList<>(); + } + + public boolean hasErrors() { + return !errors.isEmpty(); + } + + public ArrayList getErrors() { + return this.errors; + } + + private boolean gatherError(boolean c, String m) { + if(!c) + errors.add(m); + return c; + } + + private boolean required(String s) { + return (s != null && !s.isBlank()); + } + + public boolean assertMatch(String val, Pattern p, String mes) { + String param = request.getParameter(val); + boolean c = required(param) && p.matcher(param).matches(); + return gatherError(c, mes); + } + + public boolean assertInt(String val, String mes) { + return assertMatch(val, INT_PATTERN, mes); + } + + public boolean assertDouble(String val, String mes) { + return assertMatch(val, DOUBLE_PATTERN, mes); + } + + public boolean assertEmail(String val, String mes) { + Pattern p = Pattern.compile("^[a-zA-Z0-9_!#$%&’*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"); + return assertMatch(val, p, mes); + } + + public boolean assertInts(String val, String mes) { + String[] params = request.getParameterValues(val); + boolean allInt = Arrays.stream(params).allMatch(param -> INT_PATTERN.matcher(param).matches()); + return gatherError(allInt, mes); + } + + public boolean assertSize(String first, String second, String mes) { + String[] fList = request.getParameterValues(first); + String[] sList = request.getParameterValues(second); + return gatherError(fList.length==sList.length, mes); + } + +} diff --git a/YourSmartHouse/src/main/java/http/Validators.java b/YourSmartHouse/src/main/java/http/Validators.java new file mode 100644 index 0000000..0454ec3 --- /dev/null +++ b/YourSmartHouse/src/main/java/http/Validators.java @@ -0,0 +1,49 @@ +package http; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Pattern; + +public class Validators { + + public static RequestValidator productValidator(HttpServletRequest req) { + RequestValidator v = new RequestValidator(req); + v.assertMatch("nome", Pattern.compile("^.{1,100}$"), "Il nome non deve superare i 100 caratteri"); + v.assertMatch("descrizione", Pattern.compile("^[\\s\\S]{1,1500}$"), "La descrizione non deve superare i 1500 caratteri"); + v.assertDouble("prezzo", "Il prezzo deve essere un double"); + v.assertInt("categoria", "La categoria deve essere un intero positivo"); + v.assertInt("disponibilita", "La disponibilità deve essere un intero positivo"); + return v; + } + + public static RequestValidator categoryValidator(HttpServletRequest req) { + RequestValidator v = new RequestValidator(req); + v.assertMatch("nome", Pattern.compile("^\\w{1,30}$"), "Il nome non deve superare i 30 caratteri"); + return v; + } + + public static RequestValidator userValidator(HttpServletRequest req) { + RequestValidator v = new RequestValidator(req); + v.assertMatch("nome", Pattern.compile("^.{1,50}$"), "Il nome non deve superare i 50 caratteri"); + v.assertMatch("password", Pattern.compile("^.{8,32}$"), "La password deve essere compresa tra gli 8 e i 32 caratteri"); + v.assertEmail("email", "Formato email non valido"); + return v; + } + + public static RequestValidator userUpdateValidator(HttpServletRequest req) { + RequestValidator v = new RequestValidator(req); + v.assertMatch("nome", Pattern.compile("^.{1,50}$"), "Il nome non deve superare i 50 caratteri"); + v.assertEmail("email", "Formato email non valido"); + v.assertMatch("indirizzo", Pattern.compile("^.{1,100}$"), "L'indirizzo non deve superare i 100 caratteri"); + v.assertMatch("telefono", Pattern.compile("^\\+?\\d{10,12}$"), "Formato numero di telefono non valido"); + return v; + } + + public static RequestValidator changePasswordValidator(HttpServletRequest req) { + RequestValidator v = new RequestValidator(req); + v.assertMatch("oldPassword", Pattern.compile("^.{8,32}$"), "La password deve essere compresa tra gli 8 e i 32 caratteri"); + v.assertMatch("newPassword", Pattern.compile("^.{8,32}$"), "La password deve essere compresa tra gli 8 e i 32 caratteri"); + v.assertMatch("newPasswordConf", Pattern.compile("^.{8,32}$"), "La password deve essere compresa tra gli 8 e i 32 caratteri"); + return v; + } + +} diff --git a/YourSmartHouse/src/main/java/ordine/OrderServlet.java b/YourSmartHouse/src/main/java/ordine/OrderServlet.java new file mode 100644 index 0000000..53976a1 --- /dev/null +++ b/YourSmartHouse/src/main/java/ordine/OrderServlet.java @@ -0,0 +1,126 @@ +package ordine; + +import carrello.Carrello; +import carrello.OggettoCarrello; +import http.Controller; +import http.InvalidRequestException; +import http.Paginator; +import prodotto.Prodotto; +import prodotto.ProdottoDAO; +import utente.Utente; +import utente.UtenteDAO; +import utente.UtenteSession; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.sql.SQLException; +import java.time.LocalDate; +import java.util.ArrayList; + +@WebServlet(name = "OrderServlet", value = "/orders/*") +public class OrderServlet extends Controller { + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + String path = getPath(req); + switch (path) { + case "/": + case "/show": + String p = req.getParameter("page"); + int page = 1; + if (isNumber(p)) + page = Integer.parseInt(p); + + Paginator pag = new Paginator(page, 10); + OrdineDAO dao = new OrdineDAO(); + UtenteSession utenteSession = (UtenteSession) req.getSession(false).getAttribute("utenteSession"); + if(utenteSession == null) + throw new InvalidRequestException(401); + + ArrayList ordini = dao.fetchOrdersWithProducts(utenteSession.getId(), pag); + req.getSession(true).setAttribute("ordini", ordini); + req.getRequestDispatcher(view("utente/ordini")).forward(req, resp); + break; + case "/checkout": + Carrello c = getSessionCart(req.getSession(false)); + if(c == null) + throw new InvalidRequestException(400); + + UtenteDAO udao = new UtenteDAO(); + UtenteSession us = (UtenteSession) req.getSession(false).getAttribute("utenteSession"); + if(us == null) { + ArrayList a = new ArrayList<>(); + a.add("Accedi prima di continuare"); + req.setAttribute("back", view("utente/login")); + throw new InvalidRequestException(a, 400); + } + + Utente u = udao.fetchAccount(us.getId()); + + if(u.getIndirizzo() == null || u.getTelefono() == null) { + ArrayList a = new ArrayList<>(); + a.add("Completa il profilo prima di continuare"); + req.setAttribute("back", view("utente/update")); + req.setAttribute("user", u); + throw new InvalidRequestException(a, 400); + } + req.getSession(true).setAttribute("cart", c); + req.getSession(true).setAttribute("user", u); + req.getRequestDispatcher(view("utente/checkout")).forward(req, resp); + break; + default: + throw new InvalidRequestException(404); + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } catch (SQLException throwables) { + resp.sendError(500); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + String path = getPath(req); + switch (path) { + case "/create": + HttpSession session = req.getSession(false); + authenticate(session); + + UtenteSession se = (UtenteSession) session.getAttribute("utenteSession"); + Carrello c = getSessionCart(session); + OrdineDAO dao = new OrdineDAO(); + Ordine o = new Ordine(); + o.setUserId(se.getId()); + o.setCarrello(c); + o.setData(LocalDate.now()); + o.setPrezzo(c.getTotale()); + o.setQuantita(c.getQuantita()); + + if(dao.createOrder(o)) { + ProdottoDAO pdao = new ProdottoDAO(); + for(OggettoCarrello ogg : c.getOggetti()) { + Prodotto p = ogg.getProdotto(); + pdao.updateProduct(p.getProductId(), (p.getDisponibilita() - ogg.getQuantita())); + } + c.reset(); + resp.sendRedirect("/YourSmartHouse/orders/show"); + } + else + throw new InvalidRequestException(500); + break; + default: + throw new InvalidRequestException(404); + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } catch (SQLException throwables) { + throwables.printStackTrace(); + resp.sendError(500); + } + } +} diff --git a/YourSmartHouse/src/main/java/ordine/Ordine.java b/YourSmartHouse/src/main/java/ordine/Ordine.java new file mode 100644 index 0000000..9f75209 --- /dev/null +++ b/YourSmartHouse/src/main/java/ordine/Ordine.java @@ -0,0 +1,75 @@ +package ordine; + +import carrello.Carrello; +import prodotto.Prodotto; + +import java.time.LocalDate; +import java.util.ArrayList; + +public class Ordine { + private int orderId, quantita, userId; + private LocalDate data; + private double prezzo; + private ArrayList prodotti; + private Carrello carrello; + + public Ordine() { + super(); + } + + public int getOrderId() { + return orderId; + } + + public void setOrderId(int orderId) { + this.orderId = orderId; + } + + public LocalDate getData() { + return data; + } + + public void setData(LocalDate data) { + this.data = data; + } + + public double getPrezzo() { + return prezzo; + } + + public int getQuantita() { + return quantita; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public ArrayList getProdotti() { + return prodotti; + } + + public void setProdotti(ArrayList prodotti) { + this.prodotti = prodotti; + } + + public Carrello getCarrello() { + return carrello; + } + + public void setCarrello(Carrello carrello) { + this.carrello = carrello; + } + + public void setQuantita(int quantita) { + this.quantita = quantita; + } + + public void setPrezzo(double prezzo) { + this.prezzo = prezzo; + } +} diff --git a/YourSmartHouse/src/main/java/ordine/OrdineDAO.java b/YourSmartHouse/src/main/java/ordine/OrdineDAO.java new file mode 100644 index 0000000..a952273 --- /dev/null +++ b/YourSmartHouse/src/main/java/ordine/OrdineDAO.java @@ -0,0 +1,183 @@ +package ordine; + +import carrello.Carrello; +import carrello.OggettoCarrello; +import http.ConPool; +import http.Paginator; +import prodotto.Prodotto; + +import java.sql.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +public class OrdineDAO { + + public ArrayList fetchOrders(Paginator p) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM ordine LIMIT ?,?"); + ps.setInt(1, p.getOffset()); + ps.setInt(2, p.getLimit()); + ResultSet rs = ps.executeQuery(); + ArrayList ordini = new ArrayList<>(); + while (rs.next()) { + Ordine o = new Ordine(); + o.setOrderId(rs.getInt("orderId")); + o.setQuantita(rs.getInt("quantita")); + o.setPrezzo(rs.getDouble("prezzo")); + o.setUserId(rs.getInt("user_fk")); + o.setData(rs.getDate("data").toLocalDate()); + ordini.add(o); + } + rs.close(); + return ordini; + } + } + + public Ordine fetchOrder(int id) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM ordine WHERE orderId=?"); + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Ordine o = new Ordine(); + o.setOrderId(rs.getInt("orderId")); + o.setPrezzo(rs.getDouble("prezzo")); + o.setQuantita(rs.getInt("quantita")); + o.setData(rs.getDate("data").toLocalDate()); + o.setUserId(rs.getInt("user_fk")); + return o; + } + return null; + } + } + + public ArrayList fetchOrdersByUser(int userId) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM ordine WHERE user_fk=?"); + ps.setInt(1, userId); + ResultSet rs = ps.executeQuery(); + ArrayList ordini = new ArrayList<>(); + while (rs.next()) { + Ordine o = new Ordine(); + o.setOrderId(rs.getInt("orderId")); + o.setPrezzo(rs.getDouble("prezzo")); + o.setQuantita(rs.getInt("quantita")); + o.setData(rs.getDate("data").toLocalDate()); + o.setUserId(rs.getInt("user_fk")); + ordini.add(o); + } + return ordini; + } + } + + public boolean createOrder(Ordine o) throws SQLException { + try (Connection con = ConPool.getConnection()) { + con.setAutoCommit(false); + try ( + PreparedStatement ps = con.prepareStatement("INSERT INTO ordine (quantita, prezzo, data, user_fk) VALUES (?,?,?,?)", Statement.RETURN_GENERATED_KEYS); + PreparedStatement psAssoc = con.prepareStatement("INSERT INTO order_product (order_fk, product_fk, quantita) VALUES (?,?,?)") + ) { + ps.setInt(1, o.getQuantita()); + ps.setDouble(2, o.getPrezzo()); + ps.setDate(3, java.sql.Date.valueOf(o.getData())); + ps.setInt(4, o.getUserId()); + + int rows = ps.executeUpdate(); + int total = rows; + ResultSet setId = ps.getGeneratedKeys(); + setId.next(); + o.setOrderId(setId.getInt(1)); + for (OggettoCarrello ogg : o.getCarrello().getOggetti()) { + psAssoc.setInt(1, o.getOrderId()); + psAssoc.setInt(2, ogg.getProdotto().getProductId()); + psAssoc.setInt(3, ogg.getQuantita()); + total += psAssoc.executeUpdate(); + } + if (total == (rows + o.getCarrello().getOggetti().size())) { + con.commit(); + con.setAutoCommit(true); + return true; + } else { + con.rollback(); + con.setAutoCommit(true); + return false; + } + } + } + } + + public ArrayList fetchOrdersWithProducts(int userId, Paginator pa) throws SQLException { + try (Connection con = ConPool.getConnection()) { + String query = "SELECT * FROM order_product AS op INNER JOIN ordine ord ON op.order_fk=ord.orderId INNER JOIN prodotto pro ON op.product_fk=pro.productId LEFT JOIN categoria cat ON pro.category_fk=cat.categoryId WHERE ord.user_fk=? LIMIT ?,?"; + try(PreparedStatement ps = con.prepareStatement(query)) { + ps.setInt(1, userId); + ps.setInt(2, pa.getOffset()); + ps.setInt(3, pa.getLimit()); + ResultSet rs = ps.executeQuery(); + Map orderMap = new LinkedHashMap<>(); + while (rs.next()) { + int orderId = rs.getInt("ord.orderId"); + if (!orderMap.containsKey(orderId)) { + Ordine o = new Ordine(); + o.setPrezzo(rs.getDouble("prezzo")); + o.setQuantita(rs.getInt("quantita")); + o.setOrderId(orderId); + o.setData(rs.getDate("data").toLocalDate()); + o.setUserId(rs.getInt("user_fk")); + o.setCarrello(new Carrello(new ArrayList<>())); + orderMap.put(orderId, o); + + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("pro.nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("pro.prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + orderMap.get(orderId).getCarrello().addProduct(p, rs.getInt("quantita")); + } + else { + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("pro.nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("pro.prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + orderMap.get(orderId).getCarrello().addProduct(p, rs.getInt("quantita")); + } + } + return new ArrayList<>(orderMap.values()); + } + } + } + + public int countAll() throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM ordine"); + ResultSet rs = ps.executeQuery(); + int i = 0; + if(rs.next()) + i = rs.getInt(1); + return i; + } + } + + public double totalIncome() throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT prezzo FROM ordine"); + ResultSet rs = ps.executeQuery(); + double tot = 0; + while (rs.next()) { + tot += rs.getDouble(1); + } + rs.close(); + tot = Math.round(tot * 100); + return tot/100; + } + } + +} diff --git a/YourSmartHouse/src/main/java/prodotto/Prodotto.java b/YourSmartHouse/src/main/java/prodotto/Prodotto.java new file mode 100644 index 0000000..cb37baa --- /dev/null +++ b/YourSmartHouse/src/main/java/prodotto/Prodotto.java @@ -0,0 +1,122 @@ +package prodotto; + +import ordine.Ordine; +import org.json.JSONObject; + +import javax.servlet.http.Part; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.ArrayList; + +public class Prodotto { + private int productId, categoryId, disponibilita; + private String foto, nome, descrizione; + private double prezzo; + private ArrayList ordini; + + public Prodotto() { + super(); + } + + public int getProductId() { + return productId; + } + + public void setProductId(int productId) { + this.productId = productId; + } + + public int getCategoryId() { + return categoryId; + } + + public void setCategoryId(int categoryId) { + this.categoryId = categoryId; + } + + public String getFoto() { + return foto; + } + + public void setFoto(String foto) { + this.foto = foto; + } + + public String getNome() { + return nome; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public double getPrezzo() { + return prezzo; + } + + public void setPrezzo(double prezzo) { + this.prezzo = prezzo; + } + + public int getDisponibilita() { + return disponibilita; + } + + public void setDisponibilita(int disponibilita) { + this.disponibilita = disponibilita; + } + + public String getDescrizione() { + return descrizione; + } + + public void setDescrizione(String descrizione) { + this.descrizione = descrizione; + } + + public ArrayList getOrdini() { + return ordini; + } + + public void setOrdini(ArrayList ordini) { + this.ordini = ordini; + } + + public void writeFoto(String up, Part st) throws IOException { + try(InputStream filestream = st.getInputStream()) { + File file = new File(up+foto); + Files.copy(filestream, file.toPath()); + } + } + + public String getNomeCategoria(){ + switch (this.categoryId) { + case 1: + return "Illuminazione"; + case 2: + return "Altoparlanti"; + case 3: + return "Prese"; + case 4: + return "Sensori"; + case 5: + return "Elettrodomestici"; + case 6: + return "Sicurezza"; + default: + return null; + } + } + + public JSONObject toJSON() { + JSONObject object = new JSONObject(); + object.put("id", productId); + object.put("nome", nome); + object.put("foto", foto); + object.put("prezzo", prezzo); + return object; + } + +} diff --git a/YourSmartHouse/src/main/java/prodotto/ProdottoDAO.java b/YourSmartHouse/src/main/java/prodotto/ProdottoDAO.java new file mode 100644 index 0000000..2c21506 --- /dev/null +++ b/YourSmartHouse/src/main/java/prodotto/ProdottoDAO.java @@ -0,0 +1,207 @@ +package prodotto; + +import http.ConPool; +import http.Paginator; + +import java.sql.*; +import java.util.ArrayList; + +public class ProdottoDAO { + + public ArrayList fetchProducts(Paginator pa) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM prodotto LIMIT ?,?"); + ps.setInt(1, pa.getOffset()); + ps.setInt(2, pa.getLimit()); + ResultSet rs = ps.executeQuery(); + ArrayList prodotti = new ArrayList<>(); + while (rs.next()) { + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + prodotti.add(p); + } + rs.close(); + return prodotti; + } + } + + public ArrayList fetchProducts(String name, Paginator pa) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM prodotto WHERE nome LIKE ? LIMIT ?,?"); + String s = "%"+name.replace(" ", "%")+"%"; + ps.setString(1, s); + ps.setInt(2, pa.getOffset()); + ps.setInt(3, pa.getLimit()); + ResultSet rs = ps.executeQuery(); + ArrayList prodotti = new ArrayList<>(); + while (rs.next()) { + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + prodotti.add(p); + } + rs.close(); + return prodotti; + } + } + + public ArrayList fetchProductsByCategory(int catId, Paginator pa) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM prodotto WHERE category_fk=? LIMIT ?,?"); + ps.setInt(1, catId); + ps.setInt(2, pa.getOffset()); + ps.setInt(3, pa.getLimit()); + ResultSet rs = ps.executeQuery(); + ArrayList prodotti = new ArrayList<>(); + while (rs.next()) { + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + prodotti.add(p); + } + rs.close(); + return prodotti; + } + } + + public Prodotto fetchProduct(int id) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM prodotto WHERE productId=?"); + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + if(rs.next()) { + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + return p; + } + return null; + } + } + + public boolean createProduct(Prodotto p) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("INSERT INTO prodotto (nome, descrizione, foto, prezzo, disponibilita, category_fk) VALUES (?,?,?,?,?,?)", Statement.RETURN_GENERATED_KEYS); + ps.setString(1, p.getNome()); + ps.setString(2, p.getDescrizione()); + ps.setString(3, p.getFoto()); + ps.setDouble(4, p.getPrezzo()); + ps.setInt(5, p.getDisponibilita()); + ps.setInt(6, p.getCategoryId()); + return (ps.executeUpdate()==1); + } + } + + public boolean updateProduct(Prodotto p) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("UPDATE prodotto SET nome=?, descrizione=?, foto=?, prezzo=?, disponibilita=?, category_fk=? WHERE productId=?"); + ps.setInt(7, p.getProductId()); + ps.setString(1, p.getNome()); + ps.setString(2, p.getDescrizione()); + ps.setString(3, p.getFoto()); + ps.setDouble(4, p.getPrezzo()); + ps.setInt(5, p.getDisponibilita()); + ps.setInt(6, p.getCategoryId()); + return (ps.executeUpdate()==1); + } + } + + public boolean deleteProduct(Prodotto p ) throws SQLException{ // non elimina dal database, ma imposta disponibilità a zero elementi + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("UPDATE prodotto SET disponibilita=0 WHERE productId=?"); + ps.setInt(1, p.getProductId()); + return (ps.executeUpdate()==1); + } + } + + public int countAll() throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM prodotto"); + ResultSet rs = ps.executeQuery(); + int i = 0; + if(rs.next()) + i = rs.getInt(1); + return i; + } + } + + public int countAll(String query) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM prodotto WHERE nome LIKE ?"); + String s = "%"+query.replace(" ", "%")+"%"; + ps.setString(1, s); + ResultSet rs = ps.executeQuery(); + int i = 0; + if(rs.next()) + i = rs.getInt(1); + return i; + } + } + + public int countAllByCategory(int catId) throws SQLException { + try (Connection con = ConPool.getConnection()){ + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM categoria as cat INNER JOIN prodotto pro ON cat.categoryId=pro.category_fk WHERE cat.categoryId=?"); + ps.setInt(1, catId); + ResultSet rs = ps.executeQuery(); + int i = 0; + if(rs.next()) + i = rs.getInt(1); + return i; + } + } + + public ArrayList fetchRandomProducts(int limit) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM prodotto ORDER BY RAND() LIMIT ?"); + ps.setInt(1, limit); + ResultSet rs = ps.executeQuery(); + ArrayList prodotti = new ArrayList<>(); + while (rs.next()) { + if(rs.getInt("disponibilita") >=1) { + Prodotto p = new Prodotto(); + p.setProductId(rs.getInt("productId")); + p.setNome(rs.getString("nome")); + p.setDescrizione(rs.getString("descrizione")); + p.setFoto(rs.getString("foto")); + p.setPrezzo(rs.getDouble("prezzo")); + p.setDisponibilita(rs.getInt("disponibilita")); + p.setCategoryId(rs.getInt("category_fk")); + prodotti.add(p); + } + } + rs.close(); + return prodotti; + } + } + + public boolean updateProduct(int id, int disponibilita) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("UPDATE prodotto SET disponibilita=? WHERE productId=?"); + ps.setInt(2, id); + ps.setInt(1, disponibilita); + return (ps.executeUpdate()==1); + } + } + +} diff --git a/YourSmartHouse/src/main/java/prodotto/ProductServlet.java b/YourSmartHouse/src/main/java/prodotto/ProductServlet.java new file mode 100644 index 0000000..4baa373 --- /dev/null +++ b/YourSmartHouse/src/main/java/prodotto/ProductServlet.java @@ -0,0 +1,131 @@ +package prodotto; + +import http.*; +import org.json.JSONArray; +import org.json.JSONObject; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; + +@WebServlet(name = "ProductServlet", value = "/products/*") +public class ProductServlet extends Controller { + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + req.setCharacterEncoding("UTF-8"); + String path = getPath(req); + switch (path) { + case "/": + showAll(req, resp); + break; + case "/showAll": + showAll(req, resp); + break; + case "/show": + show(req, resp); + break; + case "/search": + search(req, resp); + break; + case "/featured/api": + ProdottoDAO pdao = new ProdottoDAO(); + ArrayList prods = pdao.fetchRandomProducts(12); + JSONObject object = new JSONObject(); + JSONArray array = new JSONArray(); + object.put("prods", array); + prods.forEach(p -> array.put(p.toJSON())); + sendJSON(resp, object); + break; + case "/search/api": + if(isAJAX(req)) { + ProdottoDAO dao = new ProdottoDAO(); + Paginator paginator = new Paginator(1, 5); + ArrayList prodotti = dao.fetchProducts(req.getParameter("query"), paginator); + JSONObject obj = new JSONObject(); + JSONArray arr = new JSONArray(); + obj.put("products", arr); + prodotti.forEach(p -> arr.put(p.toJSON())); + sendJSON(resp, obj); + break; + } + else + resp.sendError(404); + default: + resp.sendError(404); + break; + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } catch (SQLException s) { + resp.sendError(500); + } + } + + private void showAll(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvalidRequestException { + try { + int page = 1; + int pages; + ProdottoDAO prodottoDAO = new ProdottoDAO(); + ArrayList prodotti; + String pag = req.getParameter("page"); + if (isNumber(pag)) + page = Integer.parseInt(req.getParameter("page")); + + Paginator paginator = new Paginator(page, 24); + prodotti = prodottoDAO.fetchProducts(paginator); + pages = paginator.getPages(prodottoDAO.countAll()); + + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("prodotti", prodotti); + req.getRequestDispatcher(view("utente/prodotti")).forward(req, resp); + } catch (SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void show(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + showAll(req, resp); + + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = dao.fetchProduct(Integer.parseInt(id)); + req.getSession(true).setAttribute("prod", p); + req.getRequestDispatcher(view("utente/prodotto")).forward(req, resp); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void search(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + int page = 1; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + + String query = req.getParameter("query"); + if (query.isBlank()) + showAll(req, resp); + + Paginator pagi = new Paginator(page, 12); + ProdottoDAO pdao = new ProdottoDAO(); + ArrayList prods = pdao.fetchProducts(query, pagi); + int pages = pagi.getPages(pdao.countAll(query)); + + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("query", query); + req.getSession(true).setAttribute("prodotti", prods); + req.getRequestDispatcher(view("utente/search")).forward(req, resp); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + +} diff --git a/YourSmartHouse/src/main/java/utente/AccountServlet.java b/YourSmartHouse/src/main/java/utente/AccountServlet.java new file mode 100644 index 0000000..68cf395 --- /dev/null +++ b/YourSmartHouse/src/main/java/utente/AccountServlet.java @@ -0,0 +1,307 @@ +package utente; + +import http.*; +import ordine.OrdineDAO; +import prodotto.ProdottoDAO; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import java.util.ArrayList; + +@WebServlet(name = "AccountServlet", value = "/accounts/*") +public class AccountServlet extends Controller { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = getPath(req); + + switch (path) { + case "/": + case "/profile": + try { + HttpSession session = req.getSession(false); + authenticate(session); + UtenteSession u = (UtenteSession) session.getAttribute("utenteSession"); + UtenteDAO d = new UtenteDAO(); + Utente utente = d.fetchAccount(u.getId()); + req.setAttribute("user", utente); + req.getRequestDispatcher(view("utente/profilo")).forward(req, resp); + break; + } catch (InvalidRequestException | SQLException e) { + e.printStackTrace(); + } + case "/login": + req.getRequestDispatcher(view("utente/login")).forward(req, resp); + break; + case "/signup": + req.getRequestDispatcher(view("utente/signup")).forward(req, resp); + break; + case "/update": + try { + HttpSession session = req.getSession(false); + authenticate(session); + UtenteSession u = (UtenteSession) session.getAttribute("utenteSession"); + UtenteDAO d = new UtenteDAO(); + Utente utente = d.fetchAccount(u.getId()); + req.setAttribute("user", utente); + req.getRequestDispatcher(view("utente/update")).forward(req, resp); + break; + } catch (InvalidRequestException | SQLException e) { + e.printStackTrace(); + } + case "/changePassword": + try { + HttpSession session = req.getSession(false); + authenticate(session); + UtenteSession u = (UtenteSession) session.getAttribute("utenteSession"); + req.setAttribute("id", u.getId()); + req.getRequestDispatcher(view("utente/changePwd")).forward(req, resp); + break; + } catch (InvalidRequestException e) { + e.printStackTrace(); + } + case "/secret": + req.getRequestDispatcher(view("crm/secret")).forward(req, resp); + break; + case "/logout": + doPost(req, resp); + break; + default: + resp.sendError(404); + break; + } + + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + String path = getPath(req); + switch (path) { + case "/": + break; + case "/secret": + secret(req, resp); + break; + case "/login": + login(req, resp); + break; + case "/signup": + signup(req, resp); + break; + case "/update": + update(req, resp); + break; + case "/logout": + HttpSession session = req.getSession(false); + authenticate(session); + UtenteSession u = (UtenteSession) session.getAttribute("utenteSession"); + logout(req, resp, u); + break; + case "/changePassword": + changePassword(req, resp); + break; + case "/delete": + HttpSession ses = req.getSession(false); + authenticate(ses); + UtenteSession us = (UtenteSession) ses.getAttribute("utenteSession"); + UtenteDAO d = new UtenteDAO(); + d.deleteAccount(us.getId()); + logout(req, resp, us); + break; + default: + resp.sendError(404); + break; + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } catch (SQLException throwables) { + throwables.printStackTrace(); + } + } + + private void logout(HttpServletRequest req, HttpServletResponse resp, UtenteSession s) throws IOException { + HttpSession session = req.getSession(false); + String r = s.isAdmin() ? "/YourSmartHouse/accounts/secret" : "/YourSmartHouse/accounts/login"; + session.removeAttribute("utenteSession"); + session.invalidate(); + resp.sendRedirect(r); + } + + private void signup(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + req.setAttribute("back", view("utente/signup")); + RequestValidator v = Validators.userValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + Utente u = new Utente(); + UtenteDAO dao = new UtenteDAO(); + u.setNome(req.getParameter("nome")); + u.setEmail(req.getParameter("email")); + u.setAdmin(false); + String pwd = req.getParameter("password"); + String salt = u.generateSalt(); + u.setSalt(salt); + u.setPassword(salt+pwd); + if(dao.createAccount(u)) { + UtenteSession session = new UtenteSession(u); + req.getSession(true).setAttribute("utenteSession", session); + resp.sendRedirect("/YourSmartHouse"); + } + else throw new InvalidRequestException(500); + } catch (SQLException | IOException | NoSuchAlgorithmException e) { + throw new InvalidRequestException(500); + } + } + + private void login(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + Utente u = new Utente(); + UtenteDAO dao = new UtenteDAO(); + Utente tmp = dao.fetchAccount(req.getParameter("email")); + if (tmp == null){ + req.setAttribute("back", view("utente/login")); + ArrayList a = new ArrayList<>(); + a.add("Email o password non validi"); + throw new InvalidRequestException(a,400); + } + String s = tmp.getSalt(); + s += req.getParameter("password"); + u.setPassword(s); + if (!u.getPasswordHash().equals(tmp.getPasswordHash())){ + req.setAttribute("back", view("utente/login")); + ArrayList a = new ArrayList<>(); + a.add("Email o password non validi"); + throw new InvalidRequestException(a,400); + } + UtenteSession utenteSession = new UtenteSession(tmp); + req.getSession(true).setAttribute("utenteSession", utenteSession); + resp.sendRedirect("/YourSmartHouse"); + } catch (SQLException | IOException | NoSuchAlgorithmException e) { + throw new InvalidRequestException(500); + } + } + + private void secret(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + Utente u = new Utente(); + UtenteDAO dao = new UtenteDAO(); + Utente tmp = dao.fetchAccount(req.getParameter("email")); + if (tmp == null){ + req.setAttribute("back", view("crm/secret")); + ArrayList a = new ArrayList<>(); + a.add("Email o password non validi"); + throw new InvalidRequestException(a,400); + } + String s = tmp.getSalt(); + s += req.getParameter("password"); + u.setPassword(s); + if (!u.getPasswordHash().equals(tmp.getPasswordHash())){ + req.setAttribute("back", view("crm/secret")); + ArrayList a = new ArrayList<>(); + a.add("Email o password non validi"); + throw new InvalidRequestException(a,400); + } + + UtenteSession utenteSession = new UtenteSession(tmp); + if(!utenteSession.isAdmin()) + throw new InvalidRequestException(403); + req.getSession(true).setAttribute("utenteSession", utenteSession); + + ProdottoDAO pd = new ProdottoDAO(); + int nProd = pd.countAll(); + req.getSession(true).setAttribute("nProd", nProd); + + int nAcc = dao.countAll(); + req.getSession(true).setAttribute("nAcc", nAcc); + + OrdineDAO od = new OrdineDAO(); + int nOrd = od.countAll(); + double tot = od.totalIncome(); + req.getSession(true).setAttribute("nOrd", nOrd); + req.getSession(true).setAttribute("tot", tot); + + resp.sendRedirect("/YourSmartHouse/crm/dashboard"); + } catch (NoSuchAlgorithmException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void update(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + authenticate(req.getSession(false)); + req.setAttribute("back", view("utente/update")); + RequestValidator v = Validators.userUpdateValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + Utente u = new Utente(); + UtenteDAO dao = new UtenteDAO(); + u.setUserId(Integer.parseInt(req.getParameter("id"))); + u.setNome(req.getParameter("nome")); + u.setEmail(req.getParameter("email")); + u.setAdmin(false); + u.setTelefono(req.getParameter("telefono")); + u.setIndirizzo(req.getParameter("indirizzo")); + + if(dao.updateAccount(u)){ + resp.sendRedirect("/YourSmartHouse/accounts/"); + } + else + throw new InvalidRequestException(500); + } catch (SQLException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void changePassword(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + authenticate(req.getSession(false)); + req.setAttribute("back", view("utente/changePwd")); + RequestValidator v = Validators.changePasswordValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + UtenteDAO dao = new UtenteDAO(); + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + + int userId = Integer.parseInt(id); + String oldPwd = req.getParameter("oldPassword"); + String newPwd = req.getParameter("newPassword"); + String newPwdC = req.getParameter("newPasswordConf"); + if(!newPwd.equals(newPwdC)) + throw new InvalidRequestException(400); + + Utente u = dao.fetchAccount(userId); + Utente tmp = new Utente(); + + String oldpass = u.getSalt()+oldPwd; + tmp.setPassword(oldpass); + + if(!tmp.getPasswordHash().equals(u.getPasswordHash())) { + ArrayList a = new ArrayList<>(); + a.add("Password errata"); + throw new InvalidRequestException(a, 400); + } + + String newpass = u.getSalt()+newPwd; + u.setPassword(newpass); + if(dao.changePwd(u)) + req.getRequestDispatcher(view("utente/pwdConfirm")).forward(req, resp); + else + throw new InvalidRequestException(500); + } catch (SQLException | IOException | NoSuchAlgorithmException | ServletException e) { + throw new InvalidRequestException(500); + } + } + +} diff --git a/YourSmartHouse/src/main/java/utente/CRMServlet.java b/YourSmartHouse/src/main/java/utente/CRMServlet.java new file mode 100644 index 0000000..0349917 --- /dev/null +++ b/YourSmartHouse/src/main/java/utente/CRMServlet.java @@ -0,0 +1,489 @@ +package utente; + +import categoria.Categoria; +import categoria.CategoriaDAO; +import http.*; +import ordine.Ordine; +import ordine.OrdineDAO; +import prodotto.Prodotto; +import prodotto.ProdottoDAO; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; +import java.io.IOException; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; + +@MultipartConfig +@WebServlet(name = "CRMServlet", value = "/crm/*") +public class CRMServlet extends Controller { + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + authorize(req.getSession(false)); + String path = getPath(req); + switch (path) { + case "/": + req.getRequestDispatcher(view("crm/home")).forward(req, resp); + break; + case "/dashboard": + req.getRequestDispatcher(view("crm/home")).forward(req, resp); + break; + case "/products": + productShowAll(req, resp); + break; + case "/products/showAll": + productShowAll(req, resp); + break; + case "/products/show": + productShow(req, resp); + break; + case "/products/update": + productUpdateGet(req, resp); + break; + case "/products/create": + CategoriaDAO categoriaDAO = new CategoriaDAO(); + ArrayList categorie = categoriaDAO.fetchCategories(); + req.getSession(true).setAttribute("categorie", categorie); + req.getRequestDispatcher(view("prodotto/crea")).forward(req, resp); + break; + case "/products/search": + search(req, resp); + break; + case "/products/delete": + String id = req.getParameter("id"); + if (!isNumber(id)) + throw new InvalidRequestException(400); + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = new Prodotto(); + p.setProductId(Integer.parseInt(id)); + if (dao.deleteProduct(p)) + resp.sendRedirect("./showAll"); + else + resp.sendError(500); + break; + case "/categories": + categoryShowAll(req, resp); + break; + case "/categories/showAll": + categoryShowAll(req, resp); + break; + case "/categories/show": + categoryShow(req, resp); + break; + case "/categories/create": + req.getRequestDispatcher(view("categoria/crea")).forward(req, resp); + break; + case "/categories/update": + categoryUpdateGet(req, resp); + break; + case "/users/showAll": + userShowAll(req, resp); + break; + case "/users": + userShowAll(req, resp); + break; + case "/users/show": + userShow(req, resp); + break; + case "/orders": + orderShowAll(req, resp); + break; + case "/orders/show": + orderShow(req, resp); + break; + case "/orders/showAll": + orderShowAll(req, resp); + break; + case "/orders/user": + userOrderShow(req, resp); + break; + default: + resp.sendError(404); + break; + } + }catch (InvalidRequestException e) { + e.handle(req, resp); + } catch (SQLException throwables) { + resp.sendError(500); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + authorize(req.getSession(false)); + req.setCharacterEncoding("UTF-8"); + String path = getPath(req); + switch (path) { + case "/products/create": + productCreate(req, resp); + break; + case "/products/update": + productUpdatePost(req, resp); + break; + case "/categories/create": + categoryCreate(req, resp); + break; + case "/categories/update": + categoryUpdatePost(req, resp); + break; + default: + resp.sendError(404); + } + } catch (InvalidRequestException e) { + e.handle(req, resp); + } + } + + private void productShowAll(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + int page = 1; + int pages; + ProdottoDAO prodottoDAO = new ProdottoDAO(); + ArrayList prodotti; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + + String catId = req.getParameter("categoryId"); + if (isNumber(catId)) { //Se il parametro "categoryId" è presente e valido, mostro solo i prodotti di quella categoria + int cId = Integer.parseInt(catId); + Paginator paginator = new Paginator(page, 10); + CategoriaDAO cdao = new CategoriaDAO(); + prodotti = cdao.fetchCategoryWithProducts(cId, paginator).getProdotti(); + pages = paginator.getPages(prodottoDAO.countAllByCategory(cId)); + req.getSession(true).setAttribute("catId", catId); + } + else { //altrimenti mostro tutti i prodotti + req.getSession().removeAttribute("catId"); + Paginator paginator = new Paginator(page, 15); + prodotti = prodottoDAO.fetchProducts(paginator); + pages = paginator.getPages(prodottoDAO.countAll()); + } + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("prodotti", prodotti); + req.getRequestDispatcher(view("crm/prodotti")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void productShow(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = dao.fetchProduct(Integer.parseInt(id)); + req.getSession(true).setAttribute("prod", p); + req.getRequestDispatcher(view("crm/prodotto")).forward(req, resp); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void productUpdateGet(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if (!isNumber(id)) + throw new InvalidRequestException(400); + + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = dao.fetchProduct(Integer.parseInt(id)); + req.getSession(true).setAttribute("prod", p); + + CategoriaDAO categoriaDAO = new CategoriaDAO(); + ArrayList categorie = categoriaDAO.fetchCategories(); + req.getSession(true).setAttribute("categorie", categorie); + + req.getRequestDispatcher(view("prodotto/update")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void productUpdatePost(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + req.setAttribute("back", view("prodotto/update")); + RequestValidator v = Validators.productValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = new Prodotto(); + + p.setNome(req.getParameter("nome")); + p.setDescrizione(req.getParameter("descrizione")); + p.setPrezzo(Double.parseDouble(req.getParameter("prezzo"))); + p.setProductId(Integer.parseInt(id)); + p.setCategoryId(Integer.parseInt(req.getParameter("categoria"))); + p.setDisponibilita(Integer.parseInt(req.getParameter("disponibilita"))); + + Part filePart = req.getPart("foto"); + String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); + Date d = new Date(); + long l = d.getTime(); + fileName = fileName.substring(0, fileName.length()-4)+l+".jpg"; + p.setFoto(fileName); + + if(dao.updateProduct(p)){ + p.writeFoto(getUploadPath(), filePart); + resp.sendRedirect("./showAll"); + } + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + public void productCreate(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + req.setAttribute("back", view("prodotto/crea")); + RequestValidator v = Validators.productValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + ProdottoDAO dao = new ProdottoDAO(); + Prodotto p = new Prodotto(); + p.setNome(req.getParameter("nome")); + p.setCategoryId(Integer.parseInt(req.getParameter("categoria"))); + p.setDisponibilita(Integer.parseInt(req.getParameter("disponibilita"))); + p.setPrezzo(Double.parseDouble(req.getParameter("prezzo"))); + p.setDescrizione(req.getParameter("descrizione")); + + Part filePart = req.getPart("foto"); + String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); + Date d = new Date(); + long l = d.getTime(); + fileName = fileName.substring(0, fileName.length()-4)+l+".jpg"; //aggiungo timestamp per rendere unico il nome + p.setFoto(fileName); + + if(dao.createProduct(p)) { + p.writeFoto(getUploadPath(), filePart); + resp.sendRedirect("./showAll"); + } + else + throw new InvalidRequestException(500); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void categoryUpdateGet(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if (!isNumber(id)) + throw new InvalidRequestException(400); + CategoriaDAO dao = new CategoriaDAO(); + Categoria c = dao.fetchCategory(Integer.parseInt(id)); + req.getSession(true).setAttribute("cat", c); + req.getRequestDispatcher(view("categoria/update")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void categoryShow(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + CategoriaDAO dao = new CategoriaDAO(); + Categoria c = dao.fetchCategory(Integer.parseInt(id)); + req.getSession(true).setAttribute("cat", c); + req.getRequestDispatcher(view("crm/categoria")).forward(req, resp); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void categoryShowAll(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + int page = 1; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + Paginator paginator = new Paginator(page, 10); + + CategoriaDAO dao = new CategoriaDAO(); + ArrayList categorie = dao.fetchCategories(paginator); + int pages = paginator.getPages(dao.countAll()); + + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("categorie", categorie); + req.getRequestDispatcher(view("crm/categorie")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void categoryUpdatePost(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + req.setAttribute("back", view("categoria/update")); + RequestValidator v = Validators.categoryValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + CategoriaDAO dao = new CategoriaDAO(); + Categoria c = new Categoria(); + c.setCategoryId(Integer.parseInt(id)); + c.setNome(req.getParameter("nome")); + + Part filePart = req.getPart("foto"); + String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); + Date d = new Date(); + long l = d.getTime(); + fileName = fileName.substring(0, fileName.length()-4)+l+".jpg"; + c.setFoto(fileName); + if(dao.updateCategory(c)){ + c.writeFoto(getUploadPath(), filePart); + resp.sendRedirect("./showAll"); + } + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void categoryCreate(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + req.setAttribute("back", view("categoria/crea")); + RequestValidator v = Validators.categoryValidator(req); + if(v.hasErrors()) + throw new InvalidRequestException(v.getErrors(), 400); + + CategoriaDAO dao = new CategoriaDAO(); + Categoria c = new Categoria(); + c.setNome(req.getParameter("nome")); + + Part filePart = req.getPart("foto"); + String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); + Date d = new Date(); + long l = d.getTime(); + fileName = fileName.substring(0, fileName.length()-4)+l+".jpg"; + c.setFoto(fileName); + + if(dao.createCategory(c)){ + c.writeFoto(getUploadPath(), filePart); + resp.sendRedirect("./showAll"); + } + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + + private void userShowAll(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + int page = 1; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + Paginator paginator = new Paginator(page, 15); + + UtenteDAO dao = new UtenteDAO(); + ArrayList utenti = dao.fetchAccounts(paginator); + int pages = paginator.getPages(dao.countAll()); + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("utenti", utenti); + req.getRequestDispatcher(view("crm/utenti")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void userShow(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + UtenteDAO dao = new UtenteDAO(); + Utente u = dao.fetchAccount(Integer.parseInt(id)); + req.getSession(true).setAttribute("user", u); + req.getRequestDispatcher(view("crm/utente")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void orderShow(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + + OrdineDAO dao = new OrdineDAO(); + Ordine o = dao.fetchOrder(Integer.parseInt(id)); + req.getSession(true).setAttribute("order", o); + req.getRequestDispatcher(view("crm/ordine")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void userOrderShow(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + String id = req.getParameter("id"); + if(!isNumber(id)) + throw new InvalidRequestException(400); + + OrdineDAO dao = new OrdineDAO(); + ArrayList o = dao.fetchOrdersByUser(Integer.parseInt(id)); + req.getSession(true).setAttribute("ordini", o); + req.getRequestDispatcher(view("crm/ordini")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void orderShowAll(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + int page = 1; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + Paginator paginator = new Paginator(page, 15); + + OrdineDAO dao = new OrdineDAO(); + ArrayList ordini = dao.fetchOrders(paginator); + int pages = paginator.getPages(dao.countAll()); + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("ordini", ordini); + req.getRequestDispatcher(view("crm/ordini")).forward(req, resp); + } catch (SQLException | ServletException | IOException e) { + throw new InvalidRequestException(500); + } + } + + private void search(HttpServletRequest req, HttpServletResponse resp) throws InvalidRequestException { + try { + int page = 1; + String p = req.getParameter("page"); + if (isNumber(p)) + page = Integer.parseInt(req.getParameter("page")); + + Paginator pagi = new Paginator(page, 20); + ProdottoDAO pdao = new ProdottoDAO(); + ArrayList prods = pdao.fetchProducts(req.getParameter("query"), pagi); + int pages = pagi.getPages(prods.size()); + + req.getSession(true).setAttribute("pages", pages); + req.getSession(true).setAttribute("prodotti", prods); + req.getRequestDispatcher(view("crm/prodotti")).forward(req, resp); + } catch (ServletException | IOException | SQLException e) { + throw new InvalidRequestException(500); + } + } + +} diff --git a/YourSmartHouse/src/main/java/utente/Utente.java b/YourSmartHouse/src/main/java/utente/Utente.java new file mode 100644 index 0000000..65c2071 --- /dev/null +++ b/YourSmartHouse/src/main/java/utente/Utente.java @@ -0,0 +1,117 @@ +package utente; + +import ordine.Ordine; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; + +public class Utente { + private int userId; + private boolean admin; + private String email, passwordHash, nome, indirizzo, telefono, salt; + private ArrayList ordini; + + public Utente(){ + super(); + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public boolean isAdmin() { + return admin; + } + + public void setAdmin(boolean admin) { + this.admin = admin; + } + + public void setAdmin(int admin) { + this.admin = (admin == 1); + } + + public String getTelefono() { + return telefono; + } + + public void setTelefono(String telefono) { + this.telefono = telefono; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPasswordHash() { + return passwordHash; + } + + public void setPasswordHash(String passwordHash) { + this.passwordHash = passwordHash; + } + + public void setPassword(String p) throws NoSuchAlgorithmException { + MessageDigest d = MessageDigest.getInstance("SHA-512"); + byte[] h = d.digest(p.getBytes(StandardCharsets.UTF_8)); + StringBuilder b = new StringBuilder(); + for(byte by : h) { + b.append(String.format("%02x", by)); + } + this.passwordHash = b.toString(); + } + + public String generateSalt() { + SecureRandom sr = new SecureRandom(); + byte[] salt = new byte[16]; + sr.nextBytes(salt); + StringBuilder b = new StringBuilder(); + for(byte by : salt) { + b.append(String.format("%02x", by)); + } + return b.toString(); + } + + public String getNome() { + return nome; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public String getIndirizzo() { + return indirizzo; + } + + public void setIndirizzo(String indirizzo) { + this.indirizzo = indirizzo; + } + + public ArrayList getOrdini() { + return ordini; + } + + public void setOrdini(ArrayList ordini) { + this.ordini = ordini; + } + + public String getSalt() { + return salt; + } + + public void setSalt(String salt) { + this.salt = salt; + } +} diff --git a/YourSmartHouse/src/main/java/utente/UtenteDAO.java b/YourSmartHouse/src/main/java/utente/UtenteDAO.java new file mode 100644 index 0000000..3ea0b18 --- /dev/null +++ b/YourSmartHouse/src/main/java/utente/UtenteDAO.java @@ -0,0 +1,130 @@ +package utente; + +import http.ConPool; +import http.Paginator; + +import java.sql.*; +import java.util.ArrayList; + +public class UtenteDAO { + + public ArrayList fetchAccounts(Paginator p) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM utente LIMIT ?,?"); + ps.setInt(1, p.getOffset()); + ps.setInt(2, p.getLimit()); + ResultSet rs = ps.executeQuery(); + ArrayList utenti = new ArrayList<>(); + while (rs.next()) { + Utente u = new Utente(); + u.setAdmin(rs.getBoolean("admin")); + u.setEmail(rs.getString("email")); + u.setIndirizzo(rs.getString("indirizzo")); + u.setNome(rs.getString("nome")); + //u.setOrdini(rs.getArray("ordini")); + u.setPasswordHash(rs.getString("passwordHash")); + u.setSalt(rs.getString("salt")); + u.setTelefono(rs.getString("telefono")); + u.setUserId(rs.getInt("userId")); + utenti.add(u); + } + rs.close(); + return utenti; + } + } + + public Utente fetchAccount(String email) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM utente WHERE email=?"); + ps.setString(1, email); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Utente u = new Utente(); + u.setAdmin(rs.getBoolean("admin")); + u.setEmail(rs.getString("email")); + u.setIndirizzo(rs.getString("indirizzo")); + u.setNome(rs.getString("nome")); + u.setSalt(rs.getString("salt")); + u.setPasswordHash(rs.getString("passwordHash")); + u.setTelefono(rs.getString("telefono")); + u.setUserId(rs.getInt("userId")); + return u; + } + return null; + } + } + + public Utente fetchAccount(int id) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM utente WHERE userId=?"); + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Utente u = new Utente(); + u.setAdmin(rs.getBoolean("admin")); + u.setEmail(rs.getString("email")); + u.setIndirizzo(rs.getString("indirizzo")); + u.setNome(rs.getString("nome")); + u.setSalt(rs.getString("salt")); + u.setPasswordHash(rs.getString("passwordHash")); + u.setTelefono(rs.getString("telefono")); + u.setUserId(rs.getInt("userId")); + return u; + } + return null; + } + } + + public boolean createAccount(Utente u) throws SQLException { + try (Connection con = ConPool.getConnection()){ + PreparedStatement ps = con.prepareStatement("INSERT INTO utente (nome, email, passwordHash, admin, salt) VALUES(?,?,?,?,?)", Statement.RETURN_GENERATED_KEYS); + ps.setString(1, u.getNome()); + ps.setString(2, u.getEmail()); + ps.setString(3,u.getPasswordHash()); + ps.setBoolean(4, u.isAdmin()); + ps.setString(5, u.getSalt()); + return (ps.executeUpdate()==1); + } + } + + public boolean updateAccount(Utente u) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("UPDATE utente SET nome=?, indirizzo=?, telefono=?, email=? WHERE userId=?"); + ps.setInt(5, u.getUserId()); + ps.setString(1, u.getNome()); + ps.setString(2, u.getIndirizzo()); + ps.setString(3, u.getTelefono()); + ps.setString(4, u.getEmail()); + return (ps.executeUpdate()==1); + } + } + + public boolean deleteAccount(int id) throws SQLException{ + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("DELETE FROM utente WHERE userId=?"); + ps.setInt(1, id); + return (ps.executeUpdate()==1); + } + } + + public int countAll() throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM utente"); + ResultSet rs = ps.executeQuery(); + int i = 0; + if(rs.next()) + i = rs.getInt(1); + return i; + } + } + + public boolean changePwd(Utente u) throws SQLException { + try (Connection con = ConPool.getConnection()) { + PreparedStatement ps = con.prepareStatement("UPDATE utente SET passwordHash=? WHERE userId=?"); + ps.setInt(2, u.getUserId()); + ps.setString(1, u.getPasswordHash()); + return (ps.executeUpdate()==1); + } + } + +} diff --git a/YourSmartHouse/src/main/java/utente/UtenteSession.java b/YourSmartHouse/src/main/java/utente/UtenteSession.java new file mode 100644 index 0000000..8a0f880 --- /dev/null +++ b/YourSmartHouse/src/main/java/utente/UtenteSession.java @@ -0,0 +1,25 @@ +package utente; + +public class UtenteSession { + private final String nome; + private final int id; + private final boolean admin; + + public UtenteSession(Utente u) { + this.nome = u.getNome(); + this.id = u.getUserId(); + this.admin = u.isAdmin(); + } + + public String getNome() { + return nome; + } + + public int getId() { + return id; + } + + public boolean isAdmin() { + return admin; + } +} diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/lib/json-20210307.jar b/YourSmartHouse/src/main/webapp/WEB-INF/lib/json-20210307.jar new file mode 100644 index 0000000..6583ea5 Binary files /dev/null and b/YourSmartHouse/src/main/webapp/WEB-INF/lib/json-20210307.jar differ diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/lib/jstl-1.2.jar b/YourSmartHouse/src/main/webapp/WEB-INF/lib/jstl-1.2.jar new file mode 100644 index 0000000..0fd275e Binary files /dev/null and b/YourSmartHouse/src/main/webapp/WEB-INF/lib/jstl-1.2.jar differ diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/lib/mysql-connector-java-8.0.15.jar b/YourSmartHouse/src/main/webapp/WEB-INF/lib/mysql-connector-java-8.0.15.jar new file mode 100644 index 0000000..286115c Binary files /dev/null and b/YourSmartHouse/src/main/webapp/WEB-INF/lib/mysql-connector-java-8.0.15.jar differ diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/lib/tomcat-jdbc.jar b/YourSmartHouse/src/main/webapp/WEB-INF/lib/tomcat-jdbc.jar new file mode 100644 index 0000000..d8daa64 Binary files /dev/null and b/YourSmartHouse/src/main/webapp/WEB-INF/lib/tomcat-jdbc.jar differ diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/categoria/crea.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/categoria/crea.jsp new file mode 100644 index 0000000..12fc0dd --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/categoria/crea.jsp @@ -0,0 +1,40 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+
+

Crea Categoria

+
+ +
+ +

- ${mes}

+
+
+
+ + + + + +
+
+
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/categoria/update.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/categoria/update.jsp new file mode 100644 index 0000000..95d96c9 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/categoria/update.jsp @@ -0,0 +1,41 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+
+

Modifica Categoria

+
+ +
+ +

- ${mes}

+
+
+
+ + + + + + +
+
+
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/categoria.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/categoria.jsp new file mode 100644 index 0000000..6a86bb0 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/categoria.jsp @@ -0,0 +1,43 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ +
+

${cat.nome}


+

ID: ${cat.categoryId}

+ +
+ + +
+
+ +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/categorie.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/categorie.jsp new file mode 100644 index 0000000..2e238b8 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/categorie.jsp @@ -0,0 +1,55 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ + + + + + + + + + + + + + + + +
NomeFoto
${categoria.nome}${categoria.foto}
+ + + +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/home.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/home.jsp new file mode 100644 index 0000000..6a1e581 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/home.jsp @@ -0,0 +1,42 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + + +
+ +<%@include file="../partials/headerCRM.jsp"%> + +
+
+
+

Prodotti in magazzino

+

${nProd}

+

+
+

Utenti Registrati

+

${nAcc}

+

+
+

Ordini Totali

+

${nOrd}

+

+
+

Incasso Totale

+

${tot} €

+

+
+
+ +<%@include file="../partials/footer.jsp"%> + +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/ordine.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/ordine.jsp new file mode 100644 index 0000000..d38f0f0 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/ordine.jsp @@ -0,0 +1,34 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ +
+

ID: ${order.orderId}


+

+ Quantità: ${order.quantita}
+ Prezzo: ${order.prezzo}€
+ Data: ${order.data}
+ Utente: ${order.userId}
+

+
+ +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/ordini.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/ordini.jsp new file mode 100644 index 0000000..174e81a --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/ordini.jsp @@ -0,0 +1,51 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ + + + + + + + + + + + + + + + + + +
IDPrezzoDataUtente
${ordine.orderId}${ordine.prezzo}€${ordine.data}${ordine.userId}
+ + + +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/prodotti.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/prodotti.jsp new file mode 100644 index 0000000..0eec91f --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/prodotti.jsp @@ -0,0 +1,69 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ + + + + + + + + + + + + + + + + + + + + + +
NomeCategoriaPrezzoFotoDisponibilità
${prodotto.nome}${prodotto.getNomeCategoria()}${prodotto.prezzo}€${prodotto.foto} ${prodotto.disponibilita}
+ +
    + + + + + + + + + +
  • + ${page} +
  • +
    +
+ +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/prodotto.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/prodotto.jsp new file mode 100644 index 0000000..603c312 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/prodotto.jsp @@ -0,0 +1,45 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ +
+

${prod.nome}


+

ID: ${prod.productId}
Unità in magazzino: ${prod.disponibilita}

+ +

${prod.descrizione}

+
+ + +
+
+ +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/secret.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/secret.jsp new file mode 100644 index 0000000..f371f26 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/secret.jsp @@ -0,0 +1,34 @@ +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+<%@include file="../partials/header.jsp"%> +
+ +
+ <%@include file="../partials/footer.jsp"%> +
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/utente.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/utente.jsp new file mode 100644 index 0000000..aee5861 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/utente.jsp @@ -0,0 +1,58 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ + + + + + + + + + + + + + + + +
+

${user.nome}


+

+ ID: ${user.userId}
+ Email: ${user.email}
+ Indirizzo: ${ind}
+ Telefono: ${tel}
+

+
+ +
+
+ +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/utenti.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/utenti.jsp new file mode 100644 index 0000000..fc54e1d --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/crm/utenti.jsp @@ -0,0 +1,49 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+ + + + + + + + + + + + + + + + +
IDNomeEmail
${utente.userId}${utente.nome}${utente.email}
+ + + +
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/badRequest.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/badRequest.jsp new file mode 100644 index 0000000..402b4b3 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/badRequest.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + 400 Bad Request - YourSmartHouse + + + + + + + +
+
+

+ Errore 400
+ La richiesta non può essere soddisfatta a causa di errori di sintassi
+ Clicca per tornare indietro +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/forbidden.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/forbidden.jsp new file mode 100644 index 0000000..cf8cdd1 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/forbidden.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + 403 Forbidden - YourSmartHouse + + + + + + + +
+
+

+ Errore 403
+ Non hai il permesso di visualizzare questa pagina
+ Clicca per tornare indietro +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/internalServerError.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/internalServerError.jsp new file mode 100644 index 0000000..beeba99 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/internalServerError.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + 500 Internal Server Error - YourSmartHouse + + + + + + + +
+
+

+ Errore 500
+ Spiacenti, si è verificato un errore nel server
+ Clicca per tornare indietro +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/methodNotAllowed.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/methodNotAllowed.jsp new file mode 100644 index 0000000..f1fcbfb --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/methodNotAllowed.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + 405 Method Not Allowed - YourSmartHouse + + + + + + + +
+
+

+ Errore 405
+ Il metodo utilizzato per accedere a questa pagina non è consentito
+ Clicca per tornare indietro +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/notFound.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/notFound.jsp new file mode 100644 index 0000000..0cb7826 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/notFound.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + 404 Not Found - YourSmartHouse + + + + + + + +
+
+

+ Errore 404
+ Non sono riuscito a trovare la risorsa richiesta
+ Clicca per tornare indietro +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/unauthorized.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/unauthorized.jsp new file mode 100644 index 0000000..332f596 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/errors/unauthorized.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + 401 Unauthorized - YourSmartHouse + + + + + + + +
+
+

+ Errore 401
+ Non sei autorizzato a visualizzare questa pagina, prova ad accedere.
+ Clicca per tornare indietro +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/footer.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/footer.jsp new file mode 100644 index 0000000..dda8ba6 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/footer.jsp @@ -0,0 +1,59 @@ + + +
+ + +
+ \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/head.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/head.jsp new file mode 100644 index 0000000..4a0ed57 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/head.jsp @@ -0,0 +1,30 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + +${param.title} + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/header.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/header.jsp new file mode 100644 index 0000000..eae55c6 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/header.jsp @@ -0,0 +1,73 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> + + + + + + + + + + + + + + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/headerCRM.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/headerCRM.jsp new file mode 100644 index 0000000..9d46237 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/partials/headerCRM.jsp @@ -0,0 +1,57 @@ + + + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/prodotto/crea.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/prodotto/crea.jsp new file mode 100644 index 0000000..9dcf67b --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/prodotto/crea.jsp @@ -0,0 +1,52 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+
+

Crea Prodotto

+
+ +
+ +

- ${mes}

+
+
+
+ + + + + + + + + + + + + +
+
+
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/prodotto/update.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/prodotto/update.jsp new file mode 100644 index 0000000..a26d3a8 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/prodotto/update.jsp @@ -0,0 +1,53 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ + <%@include file="../partials/headerCRM.jsp"%> + +
+
+

Modifica Prodotto

+
+ +
+ +

- ${mes}

+
+
+
+ + + + + + + + + + + + + + +
+
+
+ + <%@include file="../partials/footer.jsp"%> + +
+ + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/carrello.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/carrello.jsp new file mode 100644 index 0000000..7761ef7 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/carrello.jsp @@ -0,0 +1,62 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> + +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+ + + +

Nessun oggetto nel carrello

+
+ +

Carrello

+
+ +
+

Totale: ${utenteCarrello.getTotale()}€

+ + + + + + + + + + + + + + + + + +
NomeQuantitàPrezzo
${ogg.prodotto.nome}${ogg.quantita}${ogg.totale()}€
+ + +
+
+ +
+
+
+ +
+ + <%@include file="../partials/footer.jsp"%> +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/categoria.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/categoria.jsp new file mode 100644 index 0000000..814604d --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/categoria.jsp @@ -0,0 +1,50 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+
+

${cat.nome}

+ +
+ + + +
+ + <%@include file="../partials/footer.jsp"%> +
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/changePwd.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/changePwd.jsp new file mode 100644 index 0000000..dfa8a62 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/changePwd.jsp @@ -0,0 +1,52 @@ +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> +
+ +
+ <%@include file="../partials/footer.jsp"%> +
+ + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/checkout.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/checkout.jsp new file mode 100644 index 0000000..d6f3395 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/checkout.jsp @@ -0,0 +1,59 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> + +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+
+
+

Spedisci a:

+

Nome: ${user.nome}

+

Email: ${user.email}

+

Indirizzo: ${user.indirizzo}

+

Telefono: ${user.telefono}

+
+ +
+

Riepilogo ordine:

+

Totale: ${cart.getTotale()}€

+
+ + + + + + + + + + + + + + + +
NomeQuantitàPrezzo
${ogg.prodotto.nome}${ogg.quantita}${ogg.totale()}€
+
+
+ +
+
+
+
+ + <%@include file="../partials/footer.jsp"%> +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/login.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/login.jsp new file mode 100644 index 0000000..8c9aa5e --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/login.jsp @@ -0,0 +1,35 @@ +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> +
+ +
+ <%@include file="../partials/footer.jsp"%> +
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/ordini.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/ordini.jsp new file mode 100644 index 0000000..78001c8 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/ordini.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> + +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+ + + +

Nessun ordine effettuato

+
+ + +
+ N° Ordine: ${ordine.orderId} | Effettuato il: ${ordine.data} | Prezzo: ${ordine.prezzo}€ + + + + + + + + + + + + + + + +
ProdottoQuantitàPrezzo
+ ${ogg.prodotto.nome} + ${ogg.quantita}${ogg.prodotto.prezzo}€
+
+
+
+
+ +
+ + <%@include file="../partials/footer.jsp"%> +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/prodotti.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/prodotti.jsp new file mode 100644 index 0000000..254b30b --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/prodotti.jsp @@ -0,0 +1,50 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+
+

Tutti i prodotti

+ +
+ + + +
+ + <%@include file="../partials/footer.jsp"%> +
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/prodotto.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/prodotto.jsp new file mode 100644 index 0000000..de864d0 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/prodotto.jsp @@ -0,0 +1,58 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+
+ +
+ +
+ +
+

Categoria: ${prod.getNomeCategoria()}

+

${prod.nome}

+

${prod.prezzo}€

+ +
+ + + +

Esaurito

+
+ + + + + +
+
+ +
+

${prod.descrizione}

+
+ +
+ +
+
+ + <%@include file="../partials/footer.jsp"%> +
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/profilo.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/profilo.jsp new file mode 100644 index 0000000..a59519b --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/profilo.jsp @@ -0,0 +1,78 @@ + +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + + + + + + + + + + + + + + + + + +
+ +

Il tuo account


+ + + +

Nome: ${user.nome}

+

Email: ${user.email}

+

Indirizzo: ${ind}

+

Telefono: ${tel}


+ + + +
+ +
+
+ + <%@include file="../partials/footer.jsp"%> +
+ + + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/pwdConfirm.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/pwdConfirm.jsp new file mode 100644 index 0000000..7df0f06 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/pwdConfirm.jsp @@ -0,0 +1,22 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html;charset=UTF-8" %> + + + Modifica Password - YourSmartHouse + + + + + + + +
+
+

+ Password modificata correttamente!
+ Clicca per tornare al profilo +

+
+
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/search.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/search.jsp new file mode 100644 index 0000000..0f93b02 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/search.jsp @@ -0,0 +1,57 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> + +
+
+ + +

Nessun prodotto trovato per: ${query}

+
+ +

Risultati di ricerca per: ${query}

+ +
+
+
+ + + +
+ + <%@include file="../partials/footer.jsp"%> +
+ + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/signup.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/signup.jsp new file mode 100644 index 0000000..151d14c --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/signup.jsp @@ -0,0 +1,54 @@ +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> +
+ +
+ <%@include file="../partials/footer.jsp"%> +
+ + + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/update.jsp b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/update.jsp new file mode 100644 index 0000000..a403174 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/views/utente/update.jsp @@ -0,0 +1,40 @@ +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + + + +
+ <%@include file="../partials/header.jsp"%> +
+ +
+ <%@include file="../partials/footer.jsp"%> +
+ + + diff --git a/YourSmartHouse/src/main/webapp/WEB-INF/web.xml b/YourSmartHouse/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d1ec07d --- /dev/null +++ b/YourSmartHouse/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,35 @@ + + + + + index.jsp + + + + 400 + /WEB-INF/views/errors/badRequest.jsp + + + 401 + /WEB-INF/views/errors/unauthorized.jsp + + + 403 + /WEB-INF/views/errors/forbidden.jsp + + + 404 + /WEB-INF/views/errors/notFound.jsp + + + 405 + /WEB-INF/views/errors/methodNotAllowed.jsp + + + 500 + /WEB-INF/views/errors/internalServerError.jsp + + \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/checkout.css b/YourSmartHouse/src/main/webapp/css/checkout.css new file mode 100644 index 0000000..47101d7 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/checkout.css @@ -0,0 +1,20 @@ +.chkout { + display: flex; +} +.user-details, .order-details { + width: 50%; + margin: 25px; +} +.user-details span { + font-weight: normal; +} + +@media screen and (max-width: 1086px) { + .chkout { + flex-wrap: wrap; + justify-content: center; + } + .user-details, .order-details { + width: 100%; + } +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/crm/crm.css b/YourSmartHouse/src/main/webapp/css/crm/crm.css new file mode 100644 index 0000000..bce2933 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/crm/crm.css @@ -0,0 +1,45 @@ +.grid-container { + padding: 20px; + align-items: center; + flex-grow: 1; + column-count: 1; +} + +.grid-y { + padding: 6px 5px 0 5px; + display: flex; + flex-flow: column wrap; + flex-basis: 100%; + border: 1px solid gray; + transition: .2s; +} + +.grid-y:hover { + transform: scale(1.04, 1.06); + cursor: pointer; +} + +h2 { + padding: 4px; + background-color: var(--primary); + color: var(--whitesmoke); + font-size: 24px; + text-align: center; +} + +h4 { + margin: 0; + padding: 4px; + text-align: center; + font-size: 20px; +} + +@media screen and (min-width: 770px){ + .grid-container { + column-count: 2; + } + + .grid-y { + flex-basis: 40%; + } +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/crm/show.css b/YourSmartHouse/src/main/webapp/css/crm/show.css new file mode 100644 index 0000000..5a4f15a --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/crm/show.css @@ -0,0 +1,49 @@ +.show { + width: 560px; + border: 3px solid var(--primary); + border-radius: 5px; + text-align: center; + margin-bottom: 15px; +} +.show img { + padding: 10px; + width: 540px; +} +.show-wrapper { + display: flex; + align-items: center; + justify-content: center; +} +.show-wrapper .button { + padding: 0 1px; + margin: 2px; +} +.show h3 { + padding: 6px; + font-size: 22px; +} +.show p { + padding: 5px; +} +.show h2 { + padding-top: 4px; + font-size: 20px; +} + +@media screen and (max-width: 800px){ + .show { + width: 320px; + } + .show img { + width: 300px; + } + .show h3 { + font-size: 20px; + } + .show p { + padding: 5px; + } + .show h2 { + font-size: 18px; + } +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/errorPage.css b/YourSmartHouse/src/main/webapp/css/errorPage.css new file mode 100644 index 0000000..4542eb6 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/errorPage.css @@ -0,0 +1,29 @@ +html { + height: 100%; +} +body{ + background: white; + color: var(--primary); + margin: 0; +} +#main{ + display: table; + width: 100%; + height: 100vh; + text-align: center; +} +#main:hover{ + cursor: pointer; +} +.err{ + display: table-cell; + vertical-align: middle; +} +.err h1{ + font-size: 50px; + display: inline-block; + padding-right: 12px; +} +.err h1 span{ + font-size: 20px; +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/footer.css b/YourSmartHouse/src/main/webapp/css/footer.css new file mode 100644 index 0000000..b021fb2 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/footer.css @@ -0,0 +1,64 @@ +footer { /* Posiziona il footer in basso */ + background: var(--primary); + position: absolute; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 200px; + padding-top: 14px; +} + +#footerlogo { + width: 68px; + height: 68px; +} + +.footer-content { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + text-align: center; +} + +.footer-content h3 { + font-size: 30px; + font-weight: 400; + line-height: 48px; +} + +.socials { + list-style: none; + display: flex; + align-items: center; + justify-content: center; + margin: 10px 0 18px 0; +} + +.socials li{ + margin: 0 8px; +} +.socials a{ + text-decoration: none; +} + +.footer-bottom { + width: 100%; + padding: 0 0 15px 0; + text-align: center; +} + +.footer-bottom p { + color: var(--whitesmoke) !important; + font-size: 14px; + word-spacing: 2px; +} + +.footer-content h3 { + color: var(--whitesmoke) !important; +} + +.footer-bottom a { + color: var(--whitesmoke); +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/form.css b/YourSmartHouse/src/main/webapp/css/form.css new file mode 100644 index 0000000..3ab53b7 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/form.css @@ -0,0 +1,80 @@ +body { + background-color: var(--primary); + font-size: 18px; +} +.content-wrap{ + background-color: var(--primary) !important; +} +.login form { + width: 300px; + margin-left: 20px; +} +.create form { + width: 340px; + margin-left: 20px; +} +#descrizione { + width: 340px; +} +.box h2 { + text-align: center; + padding-top: 15px; +} +form label { + display: flex; + margin-top: 20px; +} +form input { + width: 100%; + padding: 7px; + border: 1px solid gray; + border-radius: 6px; + outline: none; +} +.subm{ + height: 35px; + margin-top: 20px; + margin-bottom: 20px; + border: none; + background-color: var(--secondary); + color: white; +} +.login { + width: 340px; +} +.create { + width: 380px; +} + +.login p { + text-align: center; + padding-bottom: 5px; + font-size: 15px; + color: black; +} + +.login p a { + color: var(--primary); +} + +.alert { + display: none; +} + +@media screen and (max-width: 800px) { + .login form { + width: 250px; + } + .create form { + width: 286px; + } + #descrizione{ + width: 286px; + } + .login { + width: 280px; + } + .create { + width: 320px; + } +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/header.css b/YourSmartHouse/src/main/webapp/css/header.css new file mode 100644 index 0000000..23c605f --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/header.css @@ -0,0 +1,185 @@ +nav { + background: var(--primary); + padding: 4px 5px 2px 20px; +} + +#hlogo { + width: 94px; + height: 94px; +} + +nav ul { /* Stile degli elementi nell'header */ + display: flex; + list-style: none; + flex-wrap: wrap; + align-items: center; + justify-content: center; +} + +nav ul li.logo { + flex: 1; +} + +nav ul div.items { + display: inline-flex; + padding-right: 6px; +} + +nav ul div.items a { + text-decoration: none; + font-size: 18px; + margin-left: 10px; + color: var(--whitesmoke); +} + +nav ul div.items a:hover { + color: #feff5f; +} + +nav ul .search-icon { + position: relative; + height: 40px; + width: 420px; + display: flex; + background: var(--whitesmoke); + border-radius: 5px; +} + +nav ul .search-icon input { + height: 100%; + width: 380px; + border: none; + outline: none; + padding: 0 10px; + color: #000; + font-size: 16px; + border-radius: 5px 0 0 5px; +} + +nav ul .search-icon .s-icon { + height: 100%; + width: 40px; + position: absolute; + right: 0; + top: 0; + text-align: center; + border: 1px solid #cccccc; + border-radius: 0 5px 5px 0; +} + +nav ul .search-icon .s-icon:hover { + background: #e6e6e6; +} + +nav ul li.btn { + flex: 1; + display: none; +} + +#s-res { + position: absolute; + top: 36px; + padding: 0; + pointer-events: all; + z-index:2; + border-radius: 3px; + width: 100%; +} + +#s-res li { + list-style: none; + width: 100%; + cursor: default; + padding: 8px 12px; + border-bottom: 1px solid gray; + border-left: 1px solid gray; + border-right: 1px solid gray; + background: white; +} +#s-res li a { + font-size: 16px; + color: black; + text-decoration: none; +} +#s-res li a:hover { + color: var(--primary); +} + +@media screen and (max-width: 800px) { + + nav { + z-index: 1; + padding: 9px 40px 9px 0; + background: var(--primary); + } + + .content-wrap{ + z-index: 0; + } + + nav ul li.btn { + display: block; + margin-left: -50px; + } + + nav ul .search-icon { + width: 200px; + } + + nav ul .search-icon input { + width: 160px; + } + + nav ul div.items { + z-index: -1; /* Sposta gli elementi in profondità dietro l'header */ + position: fixed; + top: -220px; /* Elementi nascosti dalla visualizzazione */ + right: 0; + width: 100%; + background: var(--primary); + display: inline-block; + transition: top .4s; + } + + nav ul div.items.show { + z-index: 1; /* Rende gli elementi cliccabili spostandoli avanti in profondità */ + top: 60px; /* Compaiono gli elementi quando viene cliccato il menu */ + } + + nav ul div.items li { + text-align: center; + line-height: 30px; + margin: 30px 0; + } + + nav ul div.items li a { + font-size: 19px; + } + #s-res li a { + font-size: 14px; + } + #s-res li { + padding: 10px 14px; + } +} + +@media screen and (max-width: 1086px) { + nav { + padding: 10px 40px 10px 0; + } + + #hlogo { + display: none; + } + + nav ul div.items { + flex: 4; + } + nav ul .search-icon { + width: 280px; + } + + nav ul .search-icon input { + width: 240px; + } +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/home.css b/YourSmartHouse/src/main/webapp/css/home.css new file mode 100644 index 0000000..0760012 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/home.css @@ -0,0 +1,68 @@ +.categories-index, .products-index { + text-align: center; + margin-bottom: 50px; +} +.content-wrap h1 { + margin: 10px 0; +} +.categs, .prods { + display: flex; + flex-wrap: wrap; + justify-content: center; +} +.category { + margin: 20px; + border: 1px solid grey; + border-radius: 5px; + width: 380px; + height: 330px; + transition: all .5s ease; +} +.category img { + width: 100%; + padding: 2px; + margin-bottom: 10px; +} +.category a { + text-decoration: none; + color: black; +} +.category h3 { + font-size: 20px; +} + +.product { + margin: 15px 30px; + width: 300px; + height: 400px; + padding: 5px 0; + border-radius: 10px; + border: 2px solid rgba(85, 85, 85, .1); /* colore #555555 con opacità al 10% */ + transition: all .5s ease; +} +.product img { + width: 100%; + padding: 2px; + margin-bottom: 5px; +} +.product h3 { + font-size: 14px; + margin: 5px; +} +.product a { + text-decoration: none; + color: black; +} +.product p { + font-weight: bold; + font-size: 14px; + color: #555555; +} +.product:hover, .category:hover { + transform: scale(1.05) ; +} +.products-index h1 { + border-top: 2px solid rgba(11, 106, 183, .5); /* colore var(--primary) con opacità al 50% */ + border-radius: 30px; + padding-top: 15px; +} diff --git a/YourSmartHouse/src/main/webapp/css/library.css b/YourSmartHouse/src/main/webapp/css/library.css new file mode 100644 index 0000000..7d121d4 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/library.css @@ -0,0 +1,169 @@ +@font-face { + font-family: 'Poppins'; + font-weight: normal; + font-style: normal; + src: url("../fonts/Poppins-Regular.ttf"); +} +@font-face { + font-family: 'Poppins'; + font-weight: bold; + font-style: normal; + src: url("../fonts/Poppins-SemiBold.ttf"); +} +@font-face { + font-family: 'Poppins'; + font-weight: normal; + font-style: italic; + src: url("../fonts/Poppins-Italic.ttf"); +} +@font-face { + font-family: 'Poppins'; + font-weight: bold; + font-style: italic; + src: url("../fonts/Poppins-SemiBoldItalic.ttf"); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Poppins', sans-serif; +} +:root { + font-family: 'Poppins', sans-serif; + font-style: normal; + font-weight: normal; + --primary: #0b6ab7; + --secondary: #02a9da; + --success: #28a745; + --danger: #dc3545; + --warning: #ffc107; + --whitesmoke: #f3f3f3; +} + +.page-container { + position: relative; + min-height: 100vh; +} +body { + background: var(--primary); +} +.content-wrap { + background: white; + padding: 20px 20px 200px 20px; /* bottom 200px perché è l'altezza del footer */ +} + +.danger { + background: var(--danger); + margin-bottom: 5px; + border-radius: 3px; + color: white; +} +.danger h2 { + font-size: 20px; + padding: 4px; +} +.warning{ + background: var(--warning); +} +.success{ + background: var(--success); +} + +.move { + transform: translateY(320px); /* Sposta il contenuto in basso quando viene cliccato il menu */ +} +.content-wrap, footer { + transition: .4s; +} + +.paginator { + display: inline-grid; + list-style-type: none; + grid-template-columns: auto auto auto auto auto auto auto auto auto auto; + padding: 10px 0; +} +.paginator > li { + text-align: center; + width: 36px; + border: 1px solid black; + padding: .5rem; + background-color: var(--primary); +} +.paginator > li > a { + text-decoration: none; + color: var(--whitesmoke); +} +.paginator > li > a:hover { + color: #feff5f; +} + +.button { + border-radius: 4px; + padding: 4px; + height: 36px; + margin-bottom: 10px; + background: var(--primary); + color: var(--whitesmoke); +} +.button:hover, .subm:hover { + filter: brightness(90%); + cursor: pointer; +} +.box{ + height: auto; + margin: auto; + background-color: white; + border-radius: 3px; +} + +#backtotop { + display: none; + position: fixed; + bottom: 30px; + right: 50px; + z-index: 99; + border: 1px solid black; + outline: none; + background-color: var(--primary); + cursor: pointer; + padding: 15px; + border-radius: 25px; +} + +/* Filtri per cambiare il colore di un SVG forniti da https://codepen.io/sosuke/pen/Pjoqqp */ +#svg-search { + filter: brightness(0%) saturate(100%); /* colore #000000 */ + height: 19px; + width: 19px; + transform: translateY(8px); +} +#menu { + height: 28px; + width: 28px; +} +.icon { + filter: invert(99%) sepia(17%) saturate(11%) hue-rotate(211deg) brightness(113%) contrast(91%); /* colore #f3f3f3 */ + height: 20px; + width: 20px; + transform: translateY(3px); +} +.socials img{ + width: 26px; + height: 26px; +} +#em:hover { + filter: brightness(0%) saturate(100%); /* colore #000000 */ +} +#ig:hover { + filter: invert(53%) sepia(91%) saturate(3748%) hue-rotate(255deg) brightness(87%) contrast(98%); /* colore #be4bdb */ +} +#tw:hover { + filter: invert(62%) sepia(48%) saturate(5098%) hue-rotate(176deg) brightness(97%) contrast(95%); /* colore 1da1f2 */ +} +#yt:hover { + filter: invert(13%) sepia(93%) saturate(6690%) hue-rotate(2deg) brightness(98%) contrast(115%); /* colore ff0000 */ +} +#fb:hover { + filter: invert(31%) sepia(79%) saturate(2247%) hue-rotate(203deg) brightness(99%) contrast(92%); /* colore #000000 */ +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/normalize.css b/YourSmartHouse/src/main/webapp/css/normalize.css new file mode 100644 index 0000000..55600ce --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/normalize.css @@ -0,0 +1,154 @@ +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ +*, +::before, +::after { + box-sizing: border-box; +} + +html { + -moz-tab-size: 4; + tab-size: 4; +} + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; +} + +body { + font-family: + Poppins, + system-ui, + -apple-system, /* Firefox supports this but not yet `system-ui` */ + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji'; +} + +hr { + height: 0; + color: inherit; +} + +abbr[title] { + text-decoration: underline dotted; +} + +b, +strong { + font-weight: bolder; +} + +code, +kbd, +samp, +pre { + font-family: + ui-monospace, + SFMono-Regular, + Consolas, + 'Liberation Mono', + Menlo, + monospace; + font-size: 1em; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +table { + text-indent: 0; + border-color: inherit; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} + +button, +select{ + text-transform: none; +} + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +:-moz-focusring { + outline: 1px dotted ButtonText; +} + +:-moz-ui-invalid { + box-shadow: none; +} + +legend { + padding: 0; +} + +progress { + vertical-align: baseline; +} + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +[type='search'] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +summary { + display: list-item; +} \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/css/prodotto.css b/YourSmartHouse/src/main/webapp/css/prodotto.css new file mode 100644 index 0000000..b2aac10 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/prodotto.css @@ -0,0 +1,42 @@ +.prod-page { + display: flex; +} + +.img-container { + width: 35%; +} +.img-container img { + width: 100%; + padding: 5px; +} + +.elements { + padding: 10px; + width: 65%; +} +.elements h2,h3,h4 { + margin: 10px; +} +.elements a { + text-decoration: none; + color: var(--primary); +} +.elements form { + margin: 10px; +} +.elements button { + margin-left: 5px; +} + +@media screen and (max-width: 1086px) { + .prod-page { + flex-wrap: wrap; + justify-content: center; + } + .img-container{ + width: clamp(280px, 50%, 480px); + } + .elements { + width: 100%; + } +} diff --git a/YourSmartHouse/src/main/webapp/css/table.css b/YourSmartHouse/src/main/webapp/css/table.css new file mode 100644 index 0000000..640d116 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/css/table.css @@ -0,0 +1,57 @@ +.table { + width: 100%; + border-collapse: collapse; +} + +.table td, .table th { + padding: 12px 15px; + border: 1px solid #ddd; + text-align: center; + font-size: 16px; +} + +.table th { + background-color: var(--primary); + color: var(--whitesmoke); +} + +.table a{ + color: var(--primary); +} + +.table tbody tr:nth-child(even) { + color: black; + background-color: #f0f0f0; +} + +@media screen and (max-width: 800px){ + .table thead { + display: none; + } + + .table, .table tbody, .table tr, .table td { + display: block; + width: 100%; + } + + .table tr { + margin-bottom: 15px; + } + + .table td { + text-align: right; + padding-left: 50%; + position: relative; + } + + .table td::before { + content: attr(data-head); + position: absolute; + left: 0; + width: 50%; + padding-left: 15px; + font-weight: bold; + font-size: 15px; + text-align: left; + } +} \ No newline at end of file diff --git a/fonts/Poppins-Italic.ttf b/YourSmartHouse/src/main/webapp/fonts/Poppins-Italic.ttf similarity index 100% rename from fonts/Poppins-Italic.ttf rename to YourSmartHouse/src/main/webapp/fonts/Poppins-Italic.ttf diff --git a/fonts/Poppins-Regular.ttf b/YourSmartHouse/src/main/webapp/fonts/Poppins-Regular.ttf similarity index 100% rename from fonts/Poppins-Regular.ttf rename to YourSmartHouse/src/main/webapp/fonts/Poppins-Regular.ttf diff --git a/fonts/Poppins-SemiBold.ttf b/YourSmartHouse/src/main/webapp/fonts/Poppins-SemiBold.ttf similarity index 100% rename from fonts/Poppins-SemiBold.ttf rename to YourSmartHouse/src/main/webapp/fonts/Poppins-SemiBold.ttf diff --git a/fonts/Poppins-SemiBoldItalic.ttf b/YourSmartHouse/src/main/webapp/fonts/Poppins-SemiBoldItalic.ttf similarity index 100% rename from fonts/Poppins-SemiBoldItalic.ttf rename to YourSmartHouse/src/main/webapp/fonts/Poppins-SemiBoldItalic.ttf diff --git a/icons/account.svg b/YourSmartHouse/src/main/webapp/icons/account.svg similarity index 100% rename from icons/account.svg rename to YourSmartHouse/src/main/webapp/icons/account.svg diff --git a/icons/bulb.svg b/YourSmartHouse/src/main/webapp/icons/bulb.svg similarity index 100% rename from icons/bulb.svg rename to YourSmartHouse/src/main/webapp/icons/bulb.svg diff --git a/icons/cart.svg b/YourSmartHouse/src/main/webapp/icons/cart.svg similarity index 100% rename from icons/cart.svg rename to YourSmartHouse/src/main/webapp/icons/cart.svg diff --git a/icons/close.svg b/YourSmartHouse/src/main/webapp/icons/close.svg similarity index 100% rename from icons/close.svg rename to YourSmartHouse/src/main/webapp/icons/close.svg diff --git a/icons/email.svg b/YourSmartHouse/src/main/webapp/icons/email.svg similarity index 100% rename from icons/email.svg rename to YourSmartHouse/src/main/webapp/icons/email.svg diff --git a/icons/facebook.svg b/YourSmartHouse/src/main/webapp/icons/facebook.svg similarity index 100% rename from icons/facebook.svg rename to YourSmartHouse/src/main/webapp/icons/facebook.svg diff --git a/icons/favicon.ico b/YourSmartHouse/src/main/webapp/icons/favicon.ico similarity index 100% rename from icons/favicon.ico rename to YourSmartHouse/src/main/webapp/icons/favicon.ico diff --git a/icons/home.svg b/YourSmartHouse/src/main/webapp/icons/home.svg similarity index 100% rename from icons/home.svg rename to YourSmartHouse/src/main/webapp/icons/home.svg diff --git a/icons/instagram.svg b/YourSmartHouse/src/main/webapp/icons/instagram.svg similarity index 100% rename from icons/instagram.svg rename to YourSmartHouse/src/main/webapp/icons/instagram.svg diff --git a/icons/login.svg b/YourSmartHouse/src/main/webapp/icons/login.svg similarity index 100% rename from icons/login.svg rename to YourSmartHouse/src/main/webapp/icons/login.svg diff --git a/icons/logo.png b/YourSmartHouse/src/main/webapp/icons/logo.png similarity index 100% rename from icons/logo.png rename to YourSmartHouse/src/main/webapp/icons/logo.png diff --git a/icons/logo512.png b/YourSmartHouse/src/main/webapp/icons/logo512.png similarity index 100% rename from icons/logo512.png rename to YourSmartHouse/src/main/webapp/icons/logo512.png diff --git a/icons/logout.svg b/YourSmartHouse/src/main/webapp/icons/logout.svg similarity index 100% rename from icons/logout.svg rename to YourSmartHouse/src/main/webapp/icons/logout.svg diff --git a/icons/menu.svg b/YourSmartHouse/src/main/webapp/icons/menu.svg similarity index 100% rename from icons/menu.svg rename to YourSmartHouse/src/main/webapp/icons/menu.svg diff --git a/icons/search.svg b/YourSmartHouse/src/main/webapp/icons/search.svg similarity index 100% rename from icons/search.svg rename to YourSmartHouse/src/main/webapp/icons/search.svg diff --git a/icons/top.svg b/YourSmartHouse/src/main/webapp/icons/top.svg similarity index 100% rename from icons/top.svg rename to YourSmartHouse/src/main/webapp/icons/top.svg diff --git a/icons/twitter.svg b/YourSmartHouse/src/main/webapp/icons/twitter.svg similarity index 100% rename from icons/twitter.svg rename to YourSmartHouse/src/main/webapp/icons/twitter.svg diff --git a/icons/ylogo.png b/YourSmartHouse/src/main/webapp/icons/ylogo.png similarity index 100% rename from icons/ylogo.png rename to YourSmartHouse/src/main/webapp/icons/ylogo.png diff --git a/icons/youtube.svg b/YourSmartHouse/src/main/webapp/icons/youtube.svg similarity index 100% rename from icons/youtube.svg rename to YourSmartHouse/src/main/webapp/icons/youtube.svg diff --git a/YourSmartHouse/src/main/webapp/index.jsp b/YourSmartHouse/src/main/webapp/index.jsp new file mode 100644 index 0000000..a70e11c --- /dev/null +++ b/YourSmartHouse/src/main/webapp/index.jsp @@ -0,0 +1,66 @@ +<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + + + +
+<%@include file="WEB-INF/views/partials/header.jsp"%> + +
+
+ +
+ +
+ +
+
+ +<%@include file="WEB-INF/views/partials/footer.jsp"%> +
+ + + + + diff --git a/YourSmartHouse/src/main/webapp/js/crm.js b/YourSmartHouse/src/main/webapp/js/crm.js new file mode 100644 index 0000000..b9ec0fa --- /dev/null +++ b/YourSmartHouse/src/main/webapp/js/crm.js @@ -0,0 +1,16 @@ +const prods = document.getElementsByClassName("grid-y")[0]; +prods.addEventListener("click", function (){ + window.location.href = path+"/crm/products/showAll?page=1"; +}); +const users = document.getElementsByClassName("grid-y")[1]; +users.addEventListener("click", function () { + window.location.href = path+"/crm/users"; +}); +const orders = document.getElementsByClassName("grid-y")[2]; +orders.addEventListener("click", function () { + window.location.href = path+"/crm/orders"; +}); +const inc = document.getElementsByClassName("grid-y")[3]; +inc.addEventListener("click", function () { + window.location.href = path+"/crm/orders"; +}); \ No newline at end of file diff --git a/YourSmartHouse/src/main/webapp/privacyPolicy.jsp b/YourSmartHouse/src/main/webapp/privacyPolicy.jsp new file mode 100644 index 0000000..2515294 --- /dev/null +++ b/YourSmartHouse/src/main/webapp/privacyPolicy.jsp @@ -0,0 +1,197 @@ +<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %> + + + + + + + + +
+<%@include file="WEB-INF/views/partials/header.jsp"%> +
+

Privacy Policy

+

Last updated: June 09, 2021

+

This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.

+

We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy.

+

Interpretation and Definitions

+

Interpretation

+

The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.

+

Definitions

+

For the purposes of this Privacy Policy:

+
    +
  • +

    Account means a unique account created for You to access our Service or parts of our Service.

    +
  • +
  • +

    Company (referred to as either "the Company", "We", "Us" or "Our" in this Agreement) refers to YourSmartHouse.

    +
  • +
  • +

    Cookies are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses.

    +
  • +
  • +

    Country refers to: Italy

    +
  • +
  • +

    Device means any device that can access the Service such as a computer, a cellphone or a digital tablet.

    +
  • +
  • +

    Personal Data is any information that relates to an identified or identifiable individual.

    +
  • +
  • +

    Service refers to the Website.

    +
  • +
  • +

    Service Provider means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used.

    +
  • +
  • +

    Usage Data refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).

    +
  • +
  • +

    Website refers to YourSmartHouse

    +
  • +
  • +

    You means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.

    +
  • +
+

Collecting and Using Your Personal Data

+

Types of Data Collected

+

Personal Data

+

While using Our Service, We may ask You to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:

+
    +
  • +

    Email address

    +
  • +
  • +

    First name and last name

    +
  • +
  • +

    Phone number

    +
  • +
  • +

    Address, State, Province, ZIP/Postal code, City

    +
  • +
  • +

    Usage Data

    +
  • +
+

Usage Data

+

Usage Data is collected automatically when using the Service.

+

Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that You visit, the time and date of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data.

+

When You access the Service by or through a mobile device, We may collect certain information automatically, including, but not limited to, the type of mobile device You use, Your mobile device unique ID, the IP address of Your mobile device, Your mobile operating system, the type of mobile Internet browser You use, unique device identifiers and other diagnostic data.

+

We may also collect information that Your browser sends whenever You visit our Service or when You access the Service by or through a mobile device.

+

Tracking Technologies and Cookies

+

We use Cookies and similar tracking technologies to track the activity on Our Service and store certain information. Tracking technologies used are beacons, tags, and scripts to collect and track information and to improve and analyze Our Service. The technologies We use may include:

+
    +
  • Cookies or Browser Cookies. A cookie is a small file placed on Your Device. You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do not accept Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your browser setting so that it will refuse Cookies, our Service may use Cookies.
  • +
  • Web Beacons. Certain sections of our Service and our emails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of a certain section and verifying system and server integrity).
  • +
+

Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser. Learn more about cookies: What Are Cookies?.

+

We use both Session and Persistent Cookies for the purposes set out below:

+
    +
  • +

    Necessary / Essential Cookies

    +

    Type: Session Cookies

    +

    Administered by: Us

    +

    Purpose: These Cookies are essential to provide You with services available through the Website and to enable You to use some of its features. They help to authenticate users and prevent fraudulent use of user accounts. Without these Cookies, the services that You have asked for cannot be provided, and We only use these Cookies to provide You with those services.

    +
  • +
  • +

    Cookies Policy / Notice Acceptance Cookies

    +

    Type: Persistent Cookies

    +

    Administered by: Us

    +

    Purpose: These Cookies identify if users have accepted the use of cookies on the Website.

    +
  • +
  • +

    Functionality Cookies

    +

    Type: Persistent Cookies

    +

    Administered by: Us

    +

    Purpose: These Cookies allow us to remember choices You make when You use the Website, such as remembering your login details or language preference. The purpose of these Cookies is to provide You with a more personal experience and to avoid You having to re-enter your preferences every time You use the Website.

    +
  • +
+

For more information about the cookies we use and your choices regarding cookies, please visit our Cookies Policy or the Cookies section of our Privacy Policy.

+

Use of Your Personal Data

+

The Company may use Personal Data for the following purposes:

+
    +
  • +

    To provide and maintain our Service, including to monitor the usage of our Service.

    +
  • +
  • +

    To manage Your Account: to manage Your registration as a user of the Service. The Personal Data You provide can give You access to different functionalities of the Service that are available to You as a registered user.

    +
  • +
  • +

    For the performance of a contract: the development, compliance and undertaking of the purchase contract for the products, items or services You have purchased or of any other contract with Us through the Service.

    +
  • +
  • +

    To contact You: To contact You by email, telephone calls, SMS, or other equivalent forms of electronic communication, such as a mobile application's push notifications regarding updates or informative communications related to the functionalities, products or contracted services, including the security updates, when necessary or reasonable for their implementation.

    +
  • +
  • +

    To provide You with news, special offers and general information about other goods, services and events which we offer that are similar to those that you have already purchased or enquired about unless You have opted not to receive such information.

    +
  • +
  • +

    To manage Your requests: To attend and manage Your requests to Us.

    +
  • +
  • +

    For business transfers: We may use Your information to evaluate or conduct a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Our assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which Personal Data held by Us about our Service users is among the assets transferred.

    +
  • +
  • +

    For other purposes: We may use Your information for other purposes, such as data analysis, identifying usage trends, determining the effectiveness of our promotional campaigns and to evaluate and improve our Service, products, services, marketing and your experience.

    +
  • +
+

Retention of Your Personal Data

+

The Company will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, if we are required to retain your data to comply with applicable laws), resolve disputes, and enforce our legal agreements and policies.

+

The Company will also retain Usage Data for internal analysis purposes. Usage Data is generally retained for a shorter period of time, except when this data is used to strengthen the security or to improve the functionality of Our Service, or We are legally obligated to retain this data for longer time periods.

+

Transfer of Your Personal Data

+

Your information, including Personal Data, is processed at the Company's operating offices and in any other places where the parties involved in the processing are located. It means that this information may be transferred to — and maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from Your jurisdiction.

+

Your consent to this Privacy Policy followed by Your submission of such information represents Your agreement to that transfer.

+

The Company will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of Your data and other personal information.

+

Disclosure of Your Personal Data

+

Business Transactions

+

If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be transferred. We will provide notice before Your Personal Data is transferred and becomes subject to a different Privacy Policy.

+

Law enforcement

+

Under certain circumstances, the Company may be required to disclose Your Personal Data if required to do so by law or in response to valid requests by public authorities (e.g. a court or a government agency).

+

Other legal requirements

+

The Company may disclose Your Personal Data in the good faith belief that such action is necessary to:

+
    +
  • Comply with a legal obligation
  • +
  • Protect and defend the rights or property of the Company
  • +
  • Prevent or investigate possible wrongdoing in connection with the Service
  • +
  • Protect the personal safety of Users of the Service or the public
  • +
  • Protect against legal liability
  • +
+

Security of Your Personal Data

+

The security of Your Personal Data is important to Us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While We strive to use commercially acceptable means to protect Your Personal Data, We cannot guarantee its absolute security.

+

Children's Privacy

+

Our Service does not address anyone under the age of 13. We do not knowingly collect personally identifiable information from anyone under the age of 13. If You are a parent or guardian and You are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware that We have collected Personal Data from anyone under the age of 13 without verification of parental consent, We take steps to remove that information from Our servers.

+

If We need to rely on consent as a legal basis for processing Your information and Your country requires consent from a parent, We may require Your parent's consent before We collect and use that information.

+

Links to Other Websites

+

Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, You will be directed to that third party's site. We strongly advise You to review the Privacy Policy of every site You visit.

+

We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.

+

Changes to this Privacy Policy

+

We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy Policy on this page.

+

We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and update the "Last updated" date at the top of this Privacy Policy.

+

You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.

+

Contact Us

+

If you have any questions about this Privacy Policy, You can contact us by email: g.donisi1@studenti.unisa.it

+
+<%@include file="WEB-INF/views/partials/footer.jsp"%> +
+ + diff --git a/db.sql b/db.sql new file mode 100644 index 0000000..390cc41 --- /dev/null +++ b/db.sql @@ -0,0 +1,172 @@ +-- MySQL dump 10.13 Distrib 8.0.23, for Win64 (x86_64) +-- +-- Host: localhost Database: yoursmarthouse +-- ------------------------------------------------------ +-- Server version 8.0.23 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `categoria` +-- + +DROP TABLE IF EXISTS `categoria`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `categoria` ( + `categoryId` int NOT NULL AUTO_INCREMENT, + `nome` varchar(30) NOT NULL, + `foto` varchar(35) NOT NULL, + PRIMARY KEY (`categoryId`) +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `categoria` +-- + +LOCK TABLES `categoria` WRITE; +/*!40000 ALTER TABLE `categoria` DISABLE KEYS */; +INSERT INTO `categoria` VALUES (1,'Illuminazione','721622885064172.jpg'),(2,'Altoparlanti','371622885080660.jpg'),(3,'Prese','911622885248542.jpg'),(4,'Sensori','301622885258929.jpg'),(5,'Elettrodomestici','021622885267162.jpg'),(6,'Sicurezza','841622885280097.jpg'); +/*!40000 ALTER TABLE `categoria` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `order_product` +-- + +DROP TABLE IF EXISTS `order_product`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `order_product` ( + `order_fk` int DEFAULT NULL, + `product_fk` int DEFAULT NULL, + `quantita` int DEFAULT NULL, + KEY `order_fk_ta_idx` (`order_fk`), + KEY `product_fk_ta_idx` (`product_fk`), + CONSTRAINT `order_fk_ta` FOREIGN KEY (`order_fk`) REFERENCES `ordine` (`orderId`), + CONSTRAINT `product_fk_ta` FOREIGN KEY (`product_fk`) REFERENCES `prodotto` (`productId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `order_product` +-- + +LOCK TABLES `order_product` WRITE; +/*!40000 ALTER TABLE `order_product` DISABLE KEYS */; +INSERT INTO `order_product` VALUES (12,17,3),(12,44,1),(13,36,1),(13,14,3),(14,43,1),(14,39,1),(19,31,2),(19,58,1),(20,55,1),(20,15,5),(23,41,1),(23,34,2),(24,47,1),(24,31,3),(24,91,1),(25,76,1),(25,87,2),(25,103,1),(26,35,1); +/*!40000 ALTER TABLE `order_product` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ordine` +-- + +DROP TABLE IF EXISTS `ordine`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ordine` ( + `orderId` int NOT NULL AUTO_INCREMENT, + `quantita` int DEFAULT NULL, + `prezzo` double NOT NULL, + `data` date DEFAULT NULL, + `user_fk` int DEFAULT NULL, + PRIMARY KEY (`orderId`), + KEY `user_fk_idx` (`user_fk`), + CONSTRAINT `user_fk_cat` FOREIGN KEY (`user_fk`) REFERENCES `utente` (`userId`) +) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ordine` +-- + +LOCK TABLES `ordine` WRITE; +/*!40000 ALTER TABLE `ordine` DISABLE KEYS */; +INSERT INTO `ordine` VALUES (12,4,282.36,'2021-06-15',1),(13,4,103.67,'2021-06-15',1),(14,2,118.88,'2021-06-15',1),(19,3,55.77,'2021-06-15',5),(20,6,122.93,'2021-06-15',5),(23,3,47.81,'2021-06-15',1),(24,5,361.91,'2021-06-17',9),(25,4,244.18,'2021-06-17',10),(26,1,38.99,'2021-06-17',1); +/*!40000 ALTER TABLE `ordine` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `prodotto` +-- + +DROP TABLE IF EXISTS `prodotto`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `prodotto` ( + `productId` int NOT NULL AUTO_INCREMENT, + `nome` varchar(100) NOT NULL, + `descrizione` varchar(1500) NOT NULL, + `foto` varchar(50) NOT NULL, + `prezzo` double NOT NULL, + `disponibilita` int DEFAULT NULL, + `category_fk` int DEFAULT NULL, + PRIMARY KEY (`productId`), + KEY `category_fk_idx` (`category_fk`), + CONSTRAINT `category_fk` FOREIGN KEY (`category_fk`) REFERENCES `categoria` (`categoryId`) +) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `prodotto` +-- + +LOCK TABLES `prodotto` WRITE; +/*!40000 ALTER TABLE `prodotto` DISABLE KEYS */; +INSERT INTO `prodotto` VALUES (14,'Xiaomi Mi Desk Lamp 1S, Lampada Smart a LED da Scrivania','4 diverse modalità di illuminazione adatte a diverse esigenze e una ampia gamma di personalizzazione\r\nRegola la temperatura del colore tra 2700k e 6500k per un\'illuminazione ideale\r\nPremiato if and r dot design awards\r\nCompatibile con alexa\r\nControllo semplice e intuitivo; accendi facilmente e o o regolare la temperatura del colore e luminosità usando una manopola elegante','221622814263441.jpg',24.56,35,1),(15,'Lampadina Alexa WiFi E27,Fitop Lampadine Smart LED 10W 1000LM 90W Equivalente','[Controllo Vocale] - La lampadina intelligente WIFI a vite Fitop E27 è compatibile con Amazon Alexa (Echo / Echo dot / Echo Flex ecc.), Google Home e Siri (iOS 12 o superiore). Quindi la lampadina intelligente a LED può essere controllata da comandi vocali come: accensione/spegnimento, regolazione del colore e conversione della scena. Non è richiesto alcun hub.\r\n[Cambia Luminosità / Temperatura Colore] - La temperatura del colore della lampada intelligente può essere modificata tra 2200-6500 Kelvin e la luminosità tra 1% -100%. Dal bianco caldo alla luce bianca fredda (bianco luce diurna) per una casa accogliente e sicura.\r\n[16 Milioni di Colori e 8 Scene] - I cambi di colore danno vita alla tua casa intelligente e ci sono varie impostazioni predefinite selezionabili (lettura, relax, festa e atmosfera cromatica automatica ecc.). È inoltre possibile impostare più pianificazioni. La lampadina a LED smart si accenderà e si spegnerà automaticamente in base al tempo impostato.\r\n[Telecomando] - Facile da controllare con l\'app gratuita \"Smart Life\" / \"Tuya\" con il tuo smartphone o tablet tramite reti 2G/3G/4G/Wi-Fi ovunque. ☆ La connessione delle lampadine Wifi supporta solo WiFi da 2,4 GHz.','011622814497642.jpg',19.99,18,1),(16,'Slitinto Lampadina Smart WiFi Led E14 Lampadina a Candela, Compatibile con Alexa e Google Home','[Controllo vocale] La lampadina wifi intelligente funziona con Amazon Alexa e Google Assistant. Controlla facilmente la lampadina wifi intelligente per accendere / spegnere o attenuare / illuminare la luce con un semplice comando vocale.\r\n[Telecomando] Controlla semplicemente l\'accensione / spegnimento di tutte le tue lampadine Slitinto tramite l\'app \"Smart Life\" dal tuo telefono o tablet ovunque tu sia.\r\n[Multi-colore e controllo della luminosità] La lampadina intelligente ha 16 milioni di colori che puoi scegliere e regolare la luminosità e la saturazione, senti la tua immaginazione e crea lo spazio colore più adatto Oltre a questo ha anche 8 scene predefinite per soddisfare i tuoi diversi ambienti di illuminazione esigenze.\r\n[Impostazione dell\'ora] Imposta più programmi per accendere / spegnere le luci in base all\'ora desiderata.Come spegnere la luce di notte dopo esserti addormentato che non devi preoccuparti se hai lasciato le luci accese, accendi il luce al mattino che puoi svegliare nella luce soffusa.\r\n[Installazione facile] La lampadina Wifi con base E14 funziona solo con la rete Wi-Fi a 2,4 GHz, non è richiesto alcun hub. Per il controllo sarà sufficiente installare l\'app \"Smart life\" per smartphone.','021622814747858.jpg',16.11,42,1),(17,'Nooie Lampadina Smart con Luce Soffusa 2800k-6000k+RGBW','PROGETTATE PER LA VOSTRA SICUREZZA: gli speciali materiali con cui sono realizzate le lampadine LED Aurora evitano il surriscaldamento delle lampadine, così potrete regolare le lampadine anche a luce accesa.La lampadina intelligente è certificata FCC e UL.\r\nLUCE LED BIANCO CALDO, BIANCO FREDDO E 16 MILIONI DI COLORI: scegliete la luce più adatta alle vostre necessità (2800K-6000K), per esempio per cucinare, leggere, luce notturna, luce per i bambini, feste, ecc.\r\nCONTROLLO VOCALE O TRAMITE APP: pieno controllo delle lampadine attraverso Alexa o Google Home-è possibile accendere/spegnere o dimmerare le luci, scegliere l\'intensità del bianco o variare colore in base al proprio umore attraverso il controllo vocale o l\'app Nooie.\r\nVITA UTILE 20 ANNI+NESSUN HUB RICHIESTO: Aurora ti garantisce un risparmio per 20 anni (utilizzo medio 3 h/gg) e un risparmio del 90% sulla bolletta rispetto all\'utilizzo di lampadine ad incandescenza. Installazione in 3 minuti: scaricate l\'app, accoppiate le lampadine all\'app e utilizzatele. Non sono richiesti hub o altri collegamenti.\r\nGARANZIA 12 MESI+ SUPPORTO H 24 7/7: il nostro obiettivo più importante è la soddisfazione del cliente e seguiamo attentamente ogni prodotto Nooie.','031622814902059.jpg',18.49,21,1),(18,'Lampadina Smart E27 AISIRER Lampadina WiFi Intelligente 10W','【Controllo Vocale】AISIRER lampadine smart funziona con Amazon Alexa (Echo,Echo Dot) e Google Assistant, È possibile regolare la luminosità della lampadine smart o accendere / spegnere la lampadine smart con un comando vocale. Ad esempio, puoi dire \"alexa, accendi la luce della cucina\", solo bisogno di una frase per illuminare la vita.\r\n【Telecomando e Gruppo Lampadina】Che tu sia fuori casa o a casa, puoi controllare a distanza le tue luci con l\'app gratuita \"Smart Life\". Questa lampadina può essere controllata singolarmente o in gruppo, È possibile creare un set di lampadine sul programma, controlla le lampadine in camera da letto, soggiorno e cucina, Gestisci l\'illuminazione di tutta la casa.\r\nDimmerazione e risparmio energetico】Regola la luminosità1% -100% arbitrariamente, è possibile regolare la luminosità al 40% durante la lettura, il che aiuterà a proteggere gli occhi. La lampadina adotta il più recente design a LED, 9 W è equivalente alla luminosità della lampada a incandescenza da 60 W e il consumo di energia è basso, il che è vantaggioso per la protezione dell\'ambiente.\r\n【2 anni di garanzia】AISIRER lampadina smart è progettata per le case intelligenti. Offriamo due anni di garanzia per la qualità del prodotto. Se avete domande sulla qualità del prodotto, vi preghiamo di contattarci.','041622814930331.jpg',20.99,14,1),(19,'Philips Lighting Hue White Lampadine LED Connesse, con Bluetooth, Attacco E27','Controllabile direttamente con la funzionalità Bluetooth dal tuo smarphone attraverso l\'App dedicata\r\nPer accedere alle funzionalità completa del sistema di illuminazione smart Hue invece è necessario disporre di un Bridge Hue (venduto separatamente)\r\nAttraverso lo Hue Bridge ( venduto separatamente) potrai inoltre collegare fino a 50 apparecchi di illuminazione al tuo sistema di illuminazione smart Hue\r\nEcco le possibilità che il sistema Hue completo offre: controllo vocale, controllo intelligente anche fuori casa, facile regolazione dell\'illuminazione\r\nUtilizzalo come sveglia: imposta l\'ora, seleziona l\'effetto di illuminazione desiderato, e risvegliati con una luce graduale che riproduce quella del sole\r\nAttacco E27, dimensioni A60, diametro 61mm, altezza 110 mm, con potenza lampada equivalente a 60 W\r\nTemperatura del colore: 2700K\r\nCertificato per gli umani - Zero affanni, zero fatica, zero stress: perdere la pazienza sarà solo un ricordo','051622814986584.jpg',29.99,34,1),(20,'Philips Lighting Hue White and Color Ambiance Lampadina LED Singola Connessa, con Bluetooth','Controllabile direttamente con la funzionalità Bluetooth dal tuo smarphone attraverso l\'App dedicata\r\nPer accedere alle funzionalità completa del sistema di illuminazione smart Hue invece è necessario disporre di un Bridge Hue (venduto separatamente)\r\nAttraverso lo Hue Bridge ( venduto separatamente) potrai inoltre collegare fino a 50 apparecchi di illuminazione al tuo sistema di illuminazione smart Hue\r\nEcco le possibilità che il sistema Hue completo offre: controllo vocale, controllo intelligente anche fuori casa, facile regolazione, luce calda o fredda, luce per il relax o la concentrazione, 16 milioni di colori, luce per giochi\r\nUtilizzalo come sveglia: imposta l\'ora, seleziona l\'effetto di illuminazione desiderato, e risvegliati con una luce graduale che riproduce quella del sole','061622815040665.jpg',53.27,11,1),(21,'Philips Lighting Hue White Lampadina LED Connessa, con Bluetooth, Attacco E14','Controllabile direttamente con la funzionalità Bluetooth dal tuo smartphone attraverso l\'App dedicata\r\nPer accedere alle funzionalità completa del sistema di illuminazione smart Hue invece è necessario disporre di un Bridge Hue (venduto separatamente)\r\nAttraverso lo Hue Bridge (venduto separatamente) potrai collegare fino a 50 apparecchi di illuminazione al tuo sistema di illuminazione smart Hue\r\nSistema Hue compatibile con Alexa, Google Nest e Apple HomeKit\r\nEcco le possibilità che il sistema Hue completo offre: controllo vocale, controllo intelligente anche fuori casa, facile regolazione dell\'illuminazione\r\nUtilizzalo come sveglia: imposta l\'ora, seleziona l\'effetto di illuminazione desiderato, e risvegliati con una luce graduale che riproduce quella del sole','071622815079162.jpg',23.99,23,1),(22,'Lampadina Wifi Yeelight 16 milioni di colori (2 pack)','Bulit in Wi-Fi, Controllo facile tramite app gratuita \"Mihome\" \"Yeelight\", Non è richiesto alcun hub.\r\nComodo telecomando Wi-Fi: lampadina da 16 milioni di colori, dimmerabile e gamma di temperature di colore 1700k-6500k.\r\nTermini di utilizzo ultra lunghi fino a 11 anni, non devi nemmeno pensare a sostituire una lampadina.\r\nRisparmio energetico, design ottico professionale e LED di alta qualità offrono prestazioni di illuminazione stabili, riducono l\'abbagliamento.\r\nLavora con Amazon Alexa, supporto IFTTT Apri API Link a Google Home.','081622815119691.jpg',43.69,21,1),(23,'Xiaomi Mi LED Lampadina Colorata, WiFi (Non Richiede HUB), Compatibile con Google Home e Alexa','Mi led smart bulb è una lampadina intelligente progettata prima di tutto come una lampadina pratica, dalla forma ai materiali accuratamente selezionati. il suo design conferisce al bulbo un aspetto delicato e assicura che il calore sia facilmente dissipato. Il filtro esterno è costituito da un materiale diffusore di luce altamente trasparente e la struttura in alluminio rivestita in plastica consente una dissipazione del calore ancora maggiore, garantendo una lunga durata della lampadina','091622888763359.jpg',18.12,22,1),(24,'LE Plafoniera LED Intelligente WiFi Impermeabile, Compatibile con Alexa e Google Home','【Controllo da App o Voce】 Compatibile con APP \"Lepro LampUX\" o \"Smart Life\", puoi controllare questa plafoniera da soffitto sul tuo smartphone in qualsiasi momento ed ovunque nel mondo. ② Funziona con Amazon Alexa (echo, echo dot e altro) e Google Assistant (casa, mini e altro), ti consente di controllare la lampada con semplici comandi vocali. Mani libere!!!\r\n【Ambiente Bianco e Colorato】 Crea atmosfere diverse con oltre 16 milioni di colori, diversi toni di luce bianca (da 2700K a 6500K), completamente dimmerabili tramite app o voce.\r\n【Funzione Timer】Imposta la programma di attivazione / disattivazione in base al tuo stile di vita moderno e personalizza le luci: accendi o spegni automaticamente a orari prestabiliti.\r\n【Impermeabile IP54】 Progettato con grado di protezione IP54, resistente agli spruzzi d\'acqua, adatto per le zone umide come bagno, cucina. Comunemente utilizzato in bagno, cucina, casa, camera da letto, corridoio ecc.\r\n【Facile da Installare e Risparmio Energetico】 Basta premere il blocco scorrevole per installare questa plafoniera, collegarla alla tua rete Wi-Fi 2.4GHz. Nessun hub richiesto. La lampada da soffitto da 15 W è pari ad una lampadina da 100 W, ma consente di risparmiare energia elettrica (85%). Se non sei soddisfatto al 100% della nostra lampada da soffitto, puoi contattarci in qualsiasi momento.','101622888801242.jpg',48.39,2,1),(25,'SHILOOK Smart Plafoniera LED RGB cambia colore, con telecomando, 24W, compatibile con Alexa','【Controllo vocale e app】Con \"Amazon Alexa\" o \"Hey Google\" è possibile controllare la vostra plafoniera LED intelligente in ogni angolo della vostra casa. Se non siete a casa, potete utilizzare la lampada da soffitto Wi-Fi anche con l\'app Tuya Smart o \"Smart Life\" da lontano, che vi darà grande comodità alla vostra vita.\r\n【 Dimmerabile e RGB cambia colore 】 La luce della lampada da soffitto WiFi può essere regolata dal 100% al 10%. A seconda delle necessità, possono essere commutate 8 modalità di scena (solo tramite app). Con l\'app, oltre 16 milioni di diversi colori di luce RGB della lampada da soffitto Alexa, potrete creare un\'atmosfera romantica o simile a quella di una festa.\r\nFunzione di gruppo: grazie alla creazione di gruppi di luce nell\'app, è possibile controllare non solo una singola lampada da soffitto a LED, ma più plafoniere a LED contemporaneamente.\r\nFunzione timing: con la funzione timing è possibile accendere la lampada da soffitto intelligente automaticamente al mattino e spegnerla di notte.\r\n【Facile installazione e durata di vita di 30000 ore】 Una persona può installare la lampada da soffitto da sola e senza grandi sforzi. Con una lunga durata di 30.000 ore, questa lampada da soffitto vi risparmia la frequente sostituzione.','111622890624827.jpg',45.99,10,1),(26,'Xiaomi Mi YLXD01YL Plafoniera 450mm, Bianco [Classe di efficienza energetica A+++]','Temperatura colore regolabile per soddisfare le esigenze di illuminazione di una stanza di 25 m2\r\nModalità luce notturna morbida e modalità luce diurna, senza abbagliamento per gli occhi\r\nControllo interruttore a parete intelligente, impostato dall\'app mihome\r\nDesign sottile e sottile, parte più sottile 27mm\r\nA prova di insetti e antipolvere','121622890666959.jpg',79.89,3,1),(27,'Lumary Smart Striscia LED Alexa 2m ,RGB+Bianca Regolabile Strisce Led WIFI,Sync con Musica','【Controllo vocale】 - Potresti controllare la lampadina intelligente da Amazon Alexa, Assistente Google con istruzioni vocali per accendere / spegnere o abbassare / illuminare la tua luce, persino cambiarla con il colore specificato di cui hai bisogno.\r\n【16 milioni di colori regolabili e sincronizzazione musicale】 - La lampadina Smart WiFi Lumary supporta bianco caldo e bianco freddo da 2700K a 6500K. Questa striscia LED ha un microfono incorporato. Quando si utilizza la modalità musica, il LED della striscia Wi-Fi cambierà colore con il ritmo della musica, creando un\'atmosfera dinamica o romantica.\r\n【Telecomando APP】 - Funziona con qualsiasi router Wi-Fi, nessun hub necessario, facile da installare. Puoi controllarlo con il tuo telefono o tablet (compatibile con Android e iOS) con l\'APP APP Lumary / Smart Life (supporta solo Wi-Fi a 2,4 GHz, non Wi-Fi a 5 GHz). Puoi accendere, spegnere o cambiare i colori tramite APP anche quando sei lontano da casa. Inoltre, puoi controllare più lampadine intelligenti o creare un gruppo per tutte le lampadine intelligenti e controllarle tutte tramite APP.','131622890697268.jpg',27.86,289,1),(28,'Govee Striscia LED, Smart WiFi RGB Compatibile con Alexa e Google Assistant','Controllo vocale tramite Alexa e Google Assistant : Utilizzare antipasti conversazione semplice conversazione per accendere e spegnere le luci, impostare il colore.\r\nWiFi wireless control via App : Le Govee Home App può accensione e spegnimento luci, cambio colore e luminosità. Colori fai da te mescolando colori diversi e conservali per un uso successivo.\r\nMusic LED strip lights : C\'è un microfono incorporato che si sincronizza con qualsiasi musica che esce dall\'altoparlanti nella stanza. Questa funzione è disponibile su App e controller.\r\nCrea una luce ambientale: Non è impermeabile. Può decorare la vostra camera da letto, soffitto, armadietto della cucina, portico, scrivania e soggiorno, multicolore per Natale, Halloween, feste ecc.','141622890726398.jpg',26.99,79,1),(29,'Striscia LED Compatibile con Alexa/Google Home, SHOPLED 5M Smart WiFi LED','【Metodi di controllo triplo】 - Le strisce luminose a LED SHOPLED possono essere controllate tramite telecomando, app e Alexa. Smart App ha una forte compatibilità ed è adatta per i sistemi Android / IOS, supporta solo 2.4G / Hz. Ci sono 16 milioni di colori tra cui scegliere, convertire e armeggiare a seconda del tuo umore. Solo le strisce LED possono essere controllate dalla voce da Alexa per cambiare i colori e la luminosità.\r\n【Funzione di temporizzazione】 - La striscia luminosa WiFi Smart LED ha la funzione di temporizzazione e la funzione di memoria. È possibile impostare le strisce LED WiFi sul colore e l\'ora di accensione preferiti e l\'ultima impostazione della modalità verrà salvata per il prossimo utilizzo. Usa questa barra luminosa intelligente per rendere perfetta la tua casa intelligente.\r\n【Facile da installare e da tagliare】 - L\'adesivo auto-dissipante 3M può aderire a qualsiasi superficie pulita, asciutta e piana (sono incluse 6 staffe di montaggio fisse). Il set di illuminazione a strisce LED può aggirare gli angoli con il connettore angolare a forma di L offerto. Può essere facilmente tagliato lungo i segni di taglio in condizioni disconnesse (sono inclusi 2 connettori senza saldatura e non cablati).','151622890959721.jpg',12.75,98,1),(30,'Maxcio Striscia LED RGB WiFi, Smart 5M 5050 RGB Compatibile con Alexa e Google Home','【Telecomando tramite APP】-- Scarica l\'app gratuita \"Smartlife\" (IOS e Android), quindi aggiungi una striscia LED all\'applicazione per utilizzare le diverse funzioni. ad esempio, sincronizzare la musica, cambiare colore, condividere con la famiglia, creare una scena o la modalità DIY.【Supporta solo WiFi 2.4 GHz, IEEE 802.11b / g / n】\r\n【Controllo Vocale】-- Maxcio striscia led intelligente WiFi è compatibile con Amazon Alexa (Amazon Echo / Dot / Tap) e Google Assistant. Non solo puoi accendere o spegnere le strisce LED con i comandi vocali, ma puoi anche cambiare i colori e la luminosità.\r\n【Facile da Installare 】-- Retro della striscia led wifi ha un adesivo forte. Puoi semplicemente incollare la striscia LED ovunque al coperto con autoadesivo. Perfetto per televisione, cucina, soggiorno, bar, applique, teatro, decorazioni interne, ecc.(Questa striscia non è impermeabile)','161622891005646.jpg',23.99,35,1),(31,'Striscia LED 10M, COOLAPA LED Striscia controllata App e Telecomando','Ampia Applicazione: Le strisce LED possono essere usate per decorare camera da letto, scala, televisore, balcone, scrivania, soggiorno, giardino, bar, ristorante e hotel, in particolare per Natale, Halloween e riunioni - è un buon regalo per famiglia e amici. La striscia LED non è impermeabile.\r\nFacile da Montare: Inclusi 2 rotoli di strisce LED da 5m (10m in totale). Grazie al connettore ben cablato, otterrai un\'esperienza migliore. L’installazione è ugualmente semplice. La striscia luminosa può essere attaccata su una superficie asciutta e liscia in modo affidabile. (Pulire la superficie prima dell\'installazione)','171622891033384.jpg',15.99,16,1),(32,'Maxcio Plafoniera LED Soffitto WiFi, Smart 24W Plafoniera LED Compatibile con Alexa e Google Home','Controllo Remoto con APP -- Scansionando il codice QR nella confezione esterna o nel manuale, scarica l\'app \"Smart Life\" e collega facilmente plafoniera led al cellulare tramite la rete 2,4G, in modo da poter controllare a distanza plafoniera a led soffitto sempre e ovunque.\r\nControllo con Voce -- Plafoniere led a soffitto intelligente è compatibili con Alexa e Google Home. Puoi accendere / spegnere, aumentare / diminuire la luminosità o persino regolare le luci sul colore desiderato emettendo comandi vocali.\r\nFunzione Timer e Condivisione -- Impostare un timer e creare un programma per accendere / spegnere automaticamente la plafoniera led esterno. Condividendo i dispositivi collegati con successo con la tua famiglia e lasciare che controllino insieme la lampada da soffitto intelligente.\r\nRegolabile RGB+CW+WW -- In base alle proprie esigenze, puoi scegliere luce fredda, luce bianca ecc. Ed è possibile regolare la luminosità nell\'intervallo del 1% -100%, regolare il colore della luce su 3000K-4000K-6500K. Ci sono 8 tipi di scene da scegliere liberamente, oppure puoi DIY scena con i tuoi colori preferiti.\r\nEcologica e Impermeabile -- Maxcio plafoniere intelligente Equivalente alla tradizionale lampada a incandescenza da 200W, risparmio energetico (90%) e bassa potenza. Alta trasmittanza e materiali speciali ABS e PC, non tossici e innocui. Doppio isolamento, impermeabile IP54, spruzzi anti-appannamento','181622891072073.jpg',39.09,17,1),(33,'Lampada a sospensione LED RGB Fria, telecomando','La lampada a sospensione Fria è dotata di LED che possono brillare in bianco caldo così come in colorati colori RGB. Il telecomando incluso può essere usato per controllare sia la funzione di oscuramento che il cambio di colore. Ha anche una funzione di memoria, in modo che l\'ultima impostazione selezionata venga mantenuta la prossima volta che si accende.\r\nCon la forma a bar, la lampada a LED a sospensione è perfettamente adatta per illuminare un tavolo in sala da pranzo e visivamente molto degno di nota è la struttura cristallina all\'interno del paralume cilindrico allungato in acrilico, perché questo non è solo degno di essere visto quando la lampada è spenta, ma convince anche con un bel luccichio quando i LED sono accesi, che appare molto glamour.\r\nUn\'aggiunta ottimale per stili di vita moderni e giovanili!\r\n- con telecomando incluso\r\n- dimmerazione e cambio di colore RGB possibile\r\n- effetto memoria','191622891095495.jpg',65.89,6,1),(34,'Xiaomi Mi Smart LED Bulb','Mi lampadina LED Smart (luce bianca calda) utilizza sfere da 2700 K con una temperatura del colore bassa e una luce gialla calda. Il risultato è un\'illuminazione soffusa che ti aiuta a rilassarti e a rendere la tua casa più calda. La luminosità raggiunge i 810 lumen e può essere regolata in qualsiasi momento. Passa dall\'oscurità alla luce diurna premendo un semplice pulsante.\r\n','211622891129354.jpg',8.91,43,1),(35,'Xiaomi Mi Bedside Lamp 2','A differenza dei design tradizionali, questa lampada di seconda generazione utilizza uno stampaggio ad\r inversione avanzato e un design di dissipazione del calore ausiliario\r per emanare luce attraverso tutto il corpo della lampada. Riempi la stanza di una luce morbida per creare un\'atmosfera\r calda e rilassante, o semplicemente illumina la stanza per l\'utilizzo quotidiano.','201622891151461.jpg',38.99,18,1),(36,'Amazon Echo Dot (3ª generazione) - Altoparlante intelligente con integrazione Alexa ','Controlla la musica con la tua voce – Ascolta brani in streaming da Amazon Music, Apple Music, Spotify, TuneIn e altri servizi musicali. Con Audible puoi anche ascoltare i tuoi audiolibri preferiti.\r\nSempre pronta ad aiutarti - Chiedi ad Alexa di riprodurre musica, rispondere a domande, leggerti le ultime notizie, darti le previsioni del tempo, impostare sveglie, controllare dispositivi per Casa Intelligente compatibili e molto altro.\r\nResta sempre in contatto con gli altri - Chiama e invia messaggi senza dover usare le mani a chiunque possieda un dispositivo Echo, l’App Alexa o Skype. Con la funzione Drop In, puoi anche chiamare immediatamente un dispositivo Echo compatibile che si trova in un’altra stanza.\r\nPersonalizza Alexa con le Skill - Grazie alle centinaia di Skill disponibili, Alexa diventa sempre più intelligente e nuove funzionalità e Skill vengono aggiunte costantemente. Usale per monitorare i tuoi allenamenti, giocare e molto altro.\r\nControlla i dispositivi per Casa Intelligente con la voce - Usa la tua voce per accendere la luce, regolare un termostato e controllare altri dispositivi compatibili.\r\nProgettato per tutelare la tua privacy - Echo è stato progettato con diversi elementi per la protezione e il controllo della privacy, tra cui un apposito pulsante per disattivare i microfoni.','001623237621342.jpg',29.99,77,2),(37,'Amazon Echo Studio - Altoparlante intelligente con audio Hi-Fi e Alexa','Audio che riempie la stanza - I cinque altoparlanti offrono bassi potenti, medi dinamici e alti nitidi. La tecnologia Dolby Atmos aggiunge spazialità, definizione e profondità alla musica.\r\nPronta ad aiutarti - Chiedi ad Alexa di riprodurre musica, leggerti le ultime notizie o rispondere a una domanda.\r\nControlla la musica con la tua voce - Ascolta brani in streaming da Amazon Music, Apple Music, Spotify, Deezer e altri servizi musicali.\r\nSi adatta a qualsiasi stanza - Rileva automaticamente l’acustica dell’ambiente in cui si trova, adattando la riproduzione per offrire un audio ottimale.\r\nHub per Casa Intelligente integrato - Chiedi ad Alexa di controllare i dispositivi Zigbee compatibili.\r\nResta in contatto con la tua famiglia - Usa i tuoi dispositivi Alexa come un interfono e raggiungi qualsiasi ambiente della casa con una chiamata Drop In o un Annuncio.\r\nProgettato per tutelare la tua privacy: Echo è stato progettato con diversi elementi per la protezione e il controllo della privacy, tra cui un apposito pulsante per disattivare i microfoni.','011623238555528.jpg',199.99,31,2),(38,'Amazon Echo Show 8 (2ª generazione) | Schermo intelligente HD con Alexa e telecamera da 13 MP','Alexa può mostrarti ancora più cose - Lo schermo HD da 8\", la regolazione automatica dei colori e gli altoparlanti stereo danno vita ai tuoi contenuti d\'intrattenimento. Fai una videochiamata con la telecamera da 13 MP che, grazie all\'inquadratura automatica, ti mantiene al centro dell\'immagine.\r\nSempre al centro - La nuova telecamera ti mantiene automaticamente al centro dell\'immagine durante una videochiamata. Tutto ciò che devi fare è chiedere ad Alexa di chiamare qualcuno.\r\nSemplifica la tua vita quotidiana - Echo Show 8 ti permette di tenere sott\'occhio gli appuntamenti in calendario e i promemoria, nonché di usare la voce per impostare un timer, aggiornare le tue liste, guardare un notiziario e ricevere aggiornamenti sul traffico.\r\nGestisci la tua Casa Intelligente - Scopri cosa succede mentre non sei in casa grazie alla telecamera integrata e controlla i dispositivi compatibili (come telecamere, luci e altri) usando lo schermo interattivo o la voce.\r\nTutto l\'intrattenimento che desideri - Goditi film e serie TV in HD e con audio stereo con Prime Video, Netflix e altri servizi, oppure chiedi ad Alexa di riprodurre musica da Amazon Music, Apple Music o Spotify.\r\nMostra i tuoi ricordi - Con Amazon Photos, puoi trasformare lo schermo in una cornice digitale che, grazie alla regolazione automatica dei colori, esalterà le tue foto preferite in qualsiasi condizione di luce.','021623238688922.jpg',118.86,12,2),(39,'Amazon Echo Dot (4ª generazione) - Altoparlante intelligente con Alexa','Dal design sobrio e compatto, questo dispositivo offre un suono ricco, con voci nitide e bassi bilanciati.\r\nControlla l\'intrattenimento con la tua voce - Ascolta brani in streaming da Amazon Music, Apple Music, Spotify, Deezer e altri servizi musicali. E con Musica multistanza potrai ascoltare musica, audiolibri e podcast nello stesso momento in tutta la casa.\r\nSempre pronta ad aiutarti - Chiedi ad Alexa di raccontare una barzelletta, riprodurre musica, rispondere a domande, leggerti le ultime notizie, darti le previsioni del tempo, impostare sveglie e molto altro.\r\nControlla i tuoi dispositivi per Casa Intelligente - Usa la tua voce per controllare i dispositivi compatibili e accendere la luce, regolare un termostato o chiudere la porta.\r\nResta sempre in contatto con gli altri - Effettua una chiamata senza dover usare le mani. Chiama immediatamente un dispositivo in un\'altra stanza con Drop In o annuncia a tutti che la cena è pronta.\r\nProgettato per tutelare la tua privacy - Echo Dot è stato costruito con diversi elementi per la protezione e il controllo della privacy, tra cui un apposito pulsante per disattivare i microfoni.','031623237706930.jpg',39.99,120,2),(40,'Amazon Echo Show 5 (1ª generazione) | Schermo intelligente con Alexa','Schermo compatto e intelligente da 5,5”, con Alexa sempre pronta ad aiutarti.\r\nFai una chiamata vocale o una videochiamata con amici e familiari che possiedono un dispositivo Echo compatibile o l’App Alexa.\r\nGestisci calendari, crea una Lista di cose da fare, controlla il meteo e la situazione del traffico e segui una ricetta in video.\r\nGuarda film, serie TV e notiziari. Ascolta brani musicali, stazioni radio e audiolibri.\r\nControlla dispositivi per Casa Intelligente compatibili con la voce o dallo schermo.\r\nPersonalizzalo scegliendo il tuo quadrante preferito o una foto. Crea delle Routine e imposta una sveglia per iniziare la giornata.\r\nControlla la tua privacy con l’apposito pulsante Microfono/telecamera (on/off) o il copri-telecamera integrato.','041623240878026.jpg',79.99,34,2),(41,'GOOGLE Nest Mini Grigio Chiaro','Suono più potente e avvolgente:\r\nI bassi di Google Nest Mini sono due volte più potenti di quelli del Mini originale.\r\nRealizzato pensando all\'ambiente:\r\nParte superiore in tessuto resistente ricavato al 100% da bottiglie di plastica riciclate.\r\nRiconoscimento vocale migliorato:\r\nPuoi chiedere aiuto da un capo all\'altro della stanza.\r\nFatti aiutare in casa dal tuo Assistente Google:\r\nTi basta chiedere e avrai a disposizione il meglio di Google: aggiornamenti sul meteo, notizie, risultati sportivi e molto altro. Ascolta informazioni personalizzate come i tuoi impegni, il tragitto giornaliero, i promemoria e altro ancora.','051623240913086.jpg',29.99,55,2),(42,'GOOGLE Nest Hub 2 Charcoal','Scopri Nest Hub di Google, il centro della tua casa utile:\r\nCon Nest Hub in cucina, prepara la cena e guarda Netflix o YouTube. Chiedi a Google di mostrare le tue ricette e seguile a mani libere. Riproduci musica sull\'altoparlante avanzato con YouTube Music, Spotify e altro ancora. In soggiorno, Nest Hub è l\'unico posto in cui controllare luci, TV e altri dispositivi SMA compatibili con un tocco o con la tua voce. Sul comodino, Nest Hub ti aiuta a rilassarti la notte con suoni rilassanti e svegliarti con un allarme alba. Può anche aiutare ad avere un sonno migliore grazie alla funzione Sleep Sensing.','061623240987420.jpg',99.99,3,2),(43,'GOOGLE Nest Audio Antracite','Suono pieno e avvolgente:\r\nCon il woofer, il tweeter e il software di sintonizzazione di Nest Audio, voci nitide e bassi profondi riempiranno tutta la stanza.\r\nAudio nitido:\r\nNest Audio si adatta al tuo ambiente e a qualsiasi cosa tu stia ascoltando. Così la musica sarà ancora più avvolgente. E l\'audio delle notizie, della radio e degli audiolibri sarà più nitido che mai.\r\nRiproduci in streaming dai servizi più diffusi:\r\nBasta chiedere a Google di farti ascoltare canzoni, playlist e radio dai servizi di streaming più diffusi.\r\n\"Ok Google, fammi sentire un po\' di musica\"\r\nIl suono dell\'assistente non è mai stato così potente:\r\nAd esempio, puoi dire \"Ok Google, imposta un timer per cinque minuti\" e farti aiutare in tutta la casa. Chiedi a Google informazioni su meteo, notizie e qualunque altra cosa ti venga in mente.\r\nPrivacy integrata:\r\nNest Audio è stato progettato per proteggere la tua privacy. Puoi eliminare la cronologia dicendo \"Hey Google, elimina quello che ho appena detto\".\r\nPer spegnere fisicamente i microfoni, fai scorrere l\'interruttore del microfono nella parte posteriore in modo che sia visibile la parte arancione.','071623241021792.jpg',78.89,16,2),(44,'Sonos One Generazione 2 Smart Speaker Altoparlante Wi-Fi Intelligente, con Alexa e Google Assistant','Lasciati sorprendere dall\'audio intenso e coinvolgente di uno smart speaker di seconda generazione con controllo vocale a prova di futuro, con memoria superiore e processore aggiornato\r\nControllo vocale con alexa, airplay e google assistant\r\nDisponibile in eleganti versioni nere o bianche, con finitura opaca liscia e griglia in metallo resistente per un look che si adatta a qualunque tipo di abitazione\r\nSi connette in wireless con altri speaker del tuo sonos home sound system per riprodurre la musica in una o tutte le stanze\r\nI controlli touch sono a portata di mano, la soluzione ottimale quando il telefono è nascosto o non è a portata di mano\r\nResiste all\'umidità, adatto per il bagno (o il giardino)\r\nConnetti due sonos one per creare una coppia stereo o aggiungili come speaker posteriori per un audio surround home theater sonos\r\nI Dispositivi Alexa built-in ti permettono di connetterti istantaneamente con Alexa per riprodurre musica, controllare la tua casa intelligente, ricevere informazioni, news, meteo e molto altro grazie alla voce.','081623241064448.jpg',226.89,8,2),(45,'Huawei AI Cube Router 4G, Speaker con LTE Cat-6, Amazon Alexa Integrato, Potente Sound System','Supporta lo standard 4G LTE CAT6, fino a 300 Mbps e Wi-Fi 802.11ac 2.4 GHz/5 GHz fino a 1200 Mbps, basta inserire la SIM per una connessione Internet 4G ad alta velocità in ogni momento e dovunque\r\nSound System potente: cavità sonora da 400 ml, il diaframma in alluminio rende limpidi gli alti e i medi, mentre i radiatori passivi potenziano i bassi vibranti\r\nSoftware di riconoscimento vocale a distanza rispetta il timbro originale Huawei Histen e integrazione Alexa: più di 50.000 funzionalità disponibili, supporta più di 2000 brand compatibili\r\nAspetto CMF (colore, materiale e finish): bianco opaco elegante, tessuto leggero con tessitura 3D senza cuciture visibili, l’aspetto del prodotto è ricercato e si integra con qualsiasi arredamento','091623241091101.jpg',139.99,6,2),(46,'Bose Smart Soundbar 300 con connettività Bluetooth e controllo vocale Alexa integrato, nero','Come nessun\'altra: questa elegante soundbar per TV, film, musica e giochi offre un audio ampio, una nitidezza eccezionale e bassi intensi e vanta una finitura in nero opaco con una griglia in metallo senza giunture.\r\nPrestazioni acustiche: cinque driver full-range offrono un suono ampio e nitido, rendendo la soundbar perfetta per TV, film, giochi e molto altro.\r\nAlexa integrato: Amazon Alexa e Assistente Google sono integrati in questa soundbar smart per il controllo vocale a mani libere. Un sistema con microfono antirumore acquisisce la tua voce da ogni direzione.\r\nConnettività: associa il tuo dispositivo a questa soundbar wireless per riprodurre in streaming la tua musica preferita e molto altro tramite una connessione Bluetooth, la rete Wi-Fi di casa, Apple AirPlay 2 o Spotify Connect.\r\nConfigurazione semplice: collega questa soundbar smart alla TV tramite un cavo audio ottico (incluso) o un cavo HDMI (venduto separatamente) e scarica l\'app Bose Music per una configurazione guidata.\r\nDesign elegante: questa soundbar wireless ha un\'altezza di poco superiore a 5 cm e può essere collocata davanti al televisore o montata a parete (staffe da parete vendute separatamente). Le dimensioni sono 5,6 cm A x 67,5 cm L x 10,2 cm P.\r\nEspansione del sistema: questa soundbar Bluetooth è compatibile con altri prodotti Bose per l\'ascolto multi-room o con il Bose Bass Module 500 o 700 o i Bose Surround Speakers 700.','101623241136837.jpg',359.99,11,2),(47,'Bose Portable Smart Speaker - con Controllo Vocale Alexa Integrato, Nero','All-in-one: Il diffusore smart versatile di Bose è diffusore portatile Bluetooth, diffusore per la casa e diffusore con controllo vocale; tutto in uno\r\nPortabilità: Afferra la maniglia e porta con te ovunque questo diffusore leggero (poco di 900 g), fuori casa o da una stanza all\'altra\r\nPrestazioni audio: Goditi bassi potenti e un suono profondo, chiaro e realistico a 360°, indipendentemente da dove o cosa ascolti\r\nRobusto e pronto all\'uso: questo diffusore portatile dal design robusto offre un\'autonomia della batteria fino a 12 ore e un grado di impermeabilità IPX4, pertanto non teme cadute, urti, schizzi e spruzzi\r\nDiffusore Bluetooth: In assenza del segnale Wi-Fi, puoi utilizzarlo come un diffusore Bluetooth portatile e controllarlo tramite cellulare o tablet per ascoltare qualsiasi contenuto riprodotto dal tuo dispositivo\r\nDiffusore smart: In presenza del segnale Wi-Fi, puoi controllare il diffusore con la voce e usarlo con Amazon Alexa o Assistente Google per riprodurre servizi musicali integrati come Amazon Music, Spotify e Deezer\r\nProdotti compatibili: questo diffusore Bluetooth fa parte della famiglia Bose Smart Home e interagisce con altri diffusori e soundbar smart di Bose per un\'esperienza di ascolto multi-room\r\nBose SimpleSync: Utilizza la tecnologia Bose SimpleSync per abbinare un membro della famiglia Bose Smart Home a un diffusore Bose SoundLink per la riproduzione sincronizzata della tua musica','111623241166344.jpg',235.07,21,2),(48,'TP-Link Presa Intelligente WiFi Smart Plug, Compatibile con Alexa e Google Home, 10A, 2300W','Accesso remoto - controlla i dispositivi connessi allo smart plug ovunque ci sia internet utilizzando la app tapo sul tuo smartphone\r\nPianificazione - programma lo smart plug per fornire automaticamente energia a seconda della necessità, come impostare le luci al crepuscolo e spegnerle all\'alba\r\nControllo vocale - compatibile con alexa e google assistant per controllare lo smart plug semplicemente usando la voce\r\nModalità assenza - accensione e spegnimento dei tuoi dispositivi a intervalli di tempo programmati per simulare la presenza in casa\r\nNessun hub richiesto - connettersi al router Wi-Fi esistente\r\nCarico massimo - 2300 w, 10 a; sotto severi controlli di qualità da parte del laboratorio di tp-link e certificazione da parte dell\'autorità globale\r\nNessuna presa di terra Presa europea, solo per una presa femmina di tipo C (o prese)','001623509602619.jpg',9.98,13,3),(49,'Presa Smart WiFi 16A 3680W Presa Intelligente TECKIN Spina Energy Monitor, Controllo Remoto, Timer','Miglioramento di prestazione - Il carico massimo della nuova presa intelligente Teckin è 16A 3680W, è compatibile con la maggior parte degli elettrodomestici e può essere facilmente installata e usata in pochi minuti.\r\nMonitoraggio di consumo energetico - il modulo intelligente Teckin può fornire report in tempo reale sul consumo di energia delle apparecchiature. Si può monitorare e controllare facilmente il consumo degli elettrodomestici in qualsiasi momento e ovunque, in modo da farti risparmiare energia.\r\nControllo a distanza e controllo vocale - Con l\'applicatione Smart Life puoi usare telefono o tablet per controllare più dispositivi compatibili con Android/iOS da qualsiasi luogo. E\' compatibile con Alexa, Assistente Google, SmartThings e Siri.\r\nLa funzione Timer - impostando un timer per la presa intelligente (smart plug), il dispositivo potrà spegnersi o accendersi in automatico. Ad esempio, puoi far accendere la luce al tramonto o farla spegnere all\'alba.','011623509630233.jpg',23.99,31,3),(50,'Amazon Smart Plug (presa intelligente con connettività Wi-Fi), compatibile con Alexa','Amazon Smart Plug funziona tramite Alexa e ti permette di controllare qualsiasi presa di corrente con comandi vocali.\r\nProgramma l’accensione e lo spegnimento automatico di luci, macchine del caffè e altri elettrodomestici o controllali da remoto quando sei fuori casa.\r\nFacile da configurare e usare: collegala alla presa di corrente, apri l’App Alexa e attivala con un comando vocale.\r\nNon è necessario nessun hub per Casa Intelligente: puoi configurare routine e programmi dall’App Alexa.\r\nCertificato per gli umani - Zero affanni, zero fatica, zero stress: perdere la pazienza sarà solo un ricordo. È semplice!','021623509653952.jpg',23.31,45,3),(51,'Nooie Presa Intelligente Wifi Compatibile con Alexa/Google Home, con App Controllo Remoto','【Smart Plug, Smart Buy】 Dimensioni compatte, mantiene libera la tua seconda presa. Basta inserire le nostre spine e scaricare l\'app gratuita \"Nooie\" App per iniziare la configurazione. È possibile accendere e spegnere automaticamente le lampade, gli apparecchi dei ventilatori o controllarli da remoto quando si viaggia o in ufficio purché si connettano al WiFi 2.4G.\r\n【Libera le tue mani】 Goditi il ​​tuo viaggio grazie al controllo vocale con Amazon Ecolho Alexa e Google Home dando loro so il comando vocale. Riposati nell\'angolo del divano con una comoda coperta, un tè, un libro e puoi spegnere e accendere la lampada senza alzarti. Nessun hub richiesto.\r\n【Pianificazione e timer】 La funzione Timer soddisfa le tue esigenze come spegnere le luci di Natale in 5 minuti o fra 1 ora. Le prese wifi Nooie ti aiutano anche a pianificare il giorno e la settimana. La pianificazione personalizzata può accendere automaticamente la caffettiera alle 8:00 nei giorni feriali.\r\n【Facile da usare】 Le prese Nooie godono delle opzioni \"Blocco bambini\" e \"Condivisione\". Il blocco bambini impedisce ai bambini di manomettere involontariamente la spina. Un clic e sei pronto per condividere con i tuoi amici e familiari per controllare insieme i tuoi elettrodomestici!\r\n【Sicuro ed elegante】 Realizzato con componenti di qualità, le prese intelligenti Nooie Cream sono approvate FCC ed ELT. Il design essenziale e minimalista può integrarsi in tutti gli stili di interni moderni!','031623509688015.jpg',21.11,23,3),(52,'Presa Smart WiFi 16A 3680W TECKIN Presa Intelligente Compatibile con Alexa Echo, Google Home e Siri','Controllo da Remoto: Potrai facilmente accendere o spegnere i tuoi elettrodomestici da qualsiasi luogo, attraverso il tuo smartphone collegato ad internet e l’app “Smart Life”.\r\nControllo Vocale: La presa smart TECKIN è compatibile con Amazon Alexa e Google Home Assistant and Siri. Controllerai le tue prese smart attraverso semplici comandi vocali.\r\nPianifica e Imposta il Timer: La presa smart TECKIN si accenderà e spegnerà automaticamente, in base a come la imposterai. Utilizzando il Timer, non dimenticherai più di spegnere i tuoi dispositivi durante la notte. Questo ti farà risparmiare energia e soprattutto denaro.\r\nSicuro da Usare: La nostra presa smart è duratura, il circuito di protezione integrato, assicura la massima sicurezza. E’ realizzato in ABS ignifugo ed è dotato di un chip intelligente che protegge l’impianto elettrico della tua casa.\r\nInstallazione Facile: Basterà scaricare l’app “Smart Life” da Google Play Store o Apple Store e seguire le istruzioni presenti nell’app o nel manuale utente, e in pochi minuti inizierai la tua esperienza smart.','041623509717333.jpg',31.19,5,3),(53,'Presa smart Aunics, 2 Pezzi Presa intelligente 15A compatibile con Google home, Amazon Alexa','????? ?????- ?? ? ????? alexa ??? ???? ????? ???????? ????? ?????????? ?????? ??????????? ? ??????????? ???????????????? in automatico ??? ??? ?????????, ?????? ? ???? con Amazon Alexa o Google Assistant, Google mini ???????? ?? ???? ???????????? ? ????????.\r\n????????? ??????/??????- ?? ????? smart italiana ?? ???????? ?? ??????????? ? ???? ???????????????? ??? ?? ??? ????. Le prese smart controllate da distanza da ?????? ??????? ?? ??? ????? ????, ???? ??????????? ?? ???? ???? ?? ?,????.\r\n?????? ?????????? ?????????- ????? alexa italiana realizzata ??? ?? ????? ???????? e ??????????? ??? ????? ?? ????? ?????????? ?? ???? ????????, ciabatta multipresa smart italiana senza utilizzare nessun adattatore. (Non compatibile con presa shuko)\r\n???? ???????????- ?? ???? ????? intelligente con la forma ??ù ??????? ? ???? ??????????? ??? ????????? ?? ????? ????? ?????????? e presa da muro italiane ????????? nella stessa presa.','051623509743380.jpg',20.29,0,3),(54,'Presa Smart WiFi 16A 3680W TECKIN. Monitor dell\'energia consumata, Controllo da Remoto, Timer','Misurazione del consumo energetico - La presa intelligente è dotata di un misuratore del consumo energetico per misurare il consumo energetico ed eseguire registrazioni a lungo termine. Puoi quindi verificare, tramite l’app, quanta energia consuma il dispositivo collegato alla presa. In questo modo sarà semplice trovare elettrodomestici difettosi o che incidono fortemente sui consumi e sulla bolletta.\r\nControllo remoto e vocale - La presa smart TECKIN può essere controllata non solo tramite l\'APP Smart Life, ma anche compatibile con Google Home e Amazon Alexa. Quando vuoi accendere / spegnere la presa, chiedi ad Alexa e Google di farlo.\r\nProgrammazione timer di accensione e spegnimento - Evita i consumi elettrici non necessari e risparmia energia con la massima efficienza. Tramite la programmazione On/Off potrai impostare l\'accensione o lo spegnimento programmato per tutti i tuoi elettrodomestici. La scena creata può anche essere collegata a Siri, quindi puoi usare Siri per controllare la presa.\r\nGrande Compatibilità - La presa WiFi TECKIN supporta una corrente massima di 16 A e una potenza massima di 3680 W. Funziona a casa e da remoto con l\'utilizzo di una rete Wi-Fi 2.4Ghz. Adatto per la maggior parte degli apparecchi elettronici come luce, luci decorative per le feste, ventilatore, distributore d\'acqua, scaldabagno, umidificatore e altri dispositivi dotati di funzione ON / OFF pre-impostate.','061623509773219.jpg',29.98,16,3),(55,'Hotloop Ciabatta Intelligente Wifi MultiPresa, 4 Prese AC e 4 Porte USB, Protezione da Sovraccarico','Ciabatta Smart: 4 prese e 4 porte USB controllano ogni presa singola: tramite la ciabatta WiFi con 4 prese è possibile accendere e spegnere ogni presa singolarmente tramite l\'app.\r\nTelecomando: è possibile controllare lo stato o accendere e spegnere i dispositivi della casa tramite il telefono in qualsiasi momento e ovunque, dopo aver scaricato la nostra app gratuita, purché sia presente una rete.\r\nControllo vocale: WiFi Smart Plug può essere compatibile con Amazon Alexa, Google Home e IFTTT. È possibile controllare i vostri elettrodomestici con la Smart Plug, dando comandi vocali su Amazon Alexa, Google Home o altri assistenti compatibili.\r\nImpostazioni di calendario e timer: è possibile accendere e spegnere il tempo degli elettrodomestici con l\'app in base al vostro calendario. Impostazioni intelligenti del timer consentono di risparmiare tempo e godersi la vita intelligente.\r\nDesign sicuro: il materiale è Corea LG, ABS + PC (ignifugo e ignifugo) e ha superato le certificazioni CE e RoHS. Protezione da sovraccarico integrata, protezione da sovratensione, surriscaldamento e sovratensione.','071623509797777.jpg',22.98,29,3),(56,'meross Ciabatta Intelligente Multipresa Wifi Smart 4 Schuko 4 Porte USB 2400W 10A','★APP CONTROLLO REMOTO★: questa presa multipla intelligente ha la protezione di sovratensione, ti offre più sicurezza. Con 4 smart AC e 4 porte usb, ideale per l’uso domestico e all’ufficio. Puoi accendere o spegnere ogni uscita AC e 4 porte usb in qualsiasi posti (4 porte USB non possono essere controllate separatamante)\r\n★CONTROLLO VOCALE★: compatibile con Amazon Alexa, google home e IFTTT, supporto il controllo vocale. Puoi attivare / disattivare un elettrodomestico con il comando vocale tramite Echo o Google Home\r\n★FUNZIONE TIMER★: Crea e imposta programmazioni on / off e una routine di timer di spegnimento automatico per ogni presa e quattro porte USB per funzionare automaticamente. Affinché i tuoi dispositivi possano accendersi e spegnersi da soli. Sunrise / Sunset è pronto. La funzione Random On / Off verrà aggiunta presto tramite l\'aggiornamento del firmware\r\n★INSTALLAZIONE SEMPLICE e INTUITIVA★: basta scaricare l\'app Meross e seguire i passaggi di installazione per iniziare. Il design dell\'app è semplice e intuitivo e la connessione Wi-Fi è molto stabile\r\n★QUALITÀ ECCELLENTE★: La ciabatta intelligente è realizzata in materiale PC ignifugo ed è stata approvata per la sicurezza e l\'alta qualità. Qualsiasi domanda o problema, ti preghiamo di contattarci, ti aiutiamo a risolverlo sicuramente','081623509824064.jpg',31.44,21,3),(57,'KinCam Multipresa Ciabatta Elettrica WiFi, 4 Prese e 3 USB, Controllo Vocale/Telecomando/Timer App','?【Telecomando APP】? La presa intelligente ti consente di controllare i tuoi elettrodomestici da qualsiasi parte del mondo con l\'app Smart Life. Assicurati che lo smartphone sia connesso a Internet e che la presa intelligente sia connessa al Wi-Fi domestico. Supporta la rete wireless solo a 2,4 GHz. Puoi gestire la ciabatta su più dispositivi contemporaneamente.\r\n?【Controllo Vocale】? Compatibile con Amazon Alexa, assistente domiciliare di Google per Voice Control e IFTTT.\r\n?【Funzione Timer】? Crea il tuo programma per ciascuno dei tuoi dispositivi. Attiva / disattiva i tuoi dispositivi automaticamente in momenti diversi. Evitare sovraccarichi e surriscaldamento, ridurre le bollette elettriche e prolungare la vita dei vostri prodotti. Ad esempio, è possibile impostarlo per accendere la Scaldabagno elettrico alle 06:30, spegnere la luce ogni giorno alle 23:00. Puoi anche impostare la routine del timer di spegnimento automatico.\r\n?【Sicurezza Di Garanzia E Forte Compatibilta】? Protezione sovraccarichi e sovratensione, Alta qualità materiale ABS, materiale ignifugo, la sicurezza viene sempre prima. Questo presa di corrente wifi ha approvato le certificazioni (FCC CE Rohs) garantendo una protezione completa per te e la tua attrezzatura.Controlla la maggior parte dei dispositivi come luci, frigoriferi, ventilatori, condizionatori d\'aria, TV, computer desktop e altre apparecchiature.','091623509853124.jpg',27.81,19,3),(58,'Gosund Ciabatta Smart, Multipresa WiFi, Controllo Vocale & APP, Funzione Timer, Energy Monitor','【Controllo Vocale】Controlla la ciabatta alexa con la tua voce. Compatibile con Amazon Echo, Assistente Google. Nessun hub richiesto, puoi utilizzare i comandi vocali per accendere o spegnere i tuoi dispositivi come \"Alexa, accendi l\'aria condizionata\". Libera le mani con gosund presa smart.\r\n【Controllo Remoto Globale】Puoi controllare facilmente i tuoi dispositivi da qualsiasi luogo e in qualsiasi momento tramite l\'app Gosund. Se vuoi accendere o spegnere la corrente, basta che la tua multipresa wifi sia collegata alla cassaforte e disponibile WIFI 2.4GHz(SOLO), puoi controllarla a distanza ed è disponibile E puoi anche condividere i dispositivi con la tua famiglia.\r\n【Monitoraggio dell\'Energia e Orari】Ciabatta intelligente può monitorare il consumo energetico quotidiano o mensile in tempo reale. Questo può aiutare a risparmiare energia e prolungare la vita del vostro prodotto. È inoltre possibile impostare le luci per accendere automaticamente al tramonto o spegnere automaticamente di notte prima di andare a letto. Le 3 prese universali possono essere controllate indipendentemente, e la porta USB è controllata uniformemente.\r\n【Garanzia di Sicurezza】Ciabatta wifi ha superato la certificazione CE e soddisfa i requisiti di RoHS, sicurezza e qualità.Alta qualità e alta densità: corda a cinque piedi, materiale di accensione V0, lunga durata di servizio. La protezione della tensione e l\'interruttore d\'aria possono impedire un\'energia improvvisa enorme come il fulmine','101623509883059.jpg',23.79,27,3),(59,'Nous A5 Ciabatta, misuratore consumo elettrico, protezione sovratensione multipresa da scrivania','? Presa smart multifunzione: 3 porte di ricarica USB (5 V/3,1 A) e 3 prese smart con protezione da sovratensione integrata. Le prese smart funzionano con Alexa e Google Home per il controllo vocale. Alexa, Google Home, controlla i tuoi elettrodomestici con la presa intelligente.\r\n⏳ Pianificazione e timer: telecomando per i vostri elettrodomestici da qualsiasi luogo e in qualsiasi momento. Pianificate la presa intelligente in modo che l\'elettronica venga accesa e spenta automaticamente in caso di necessità. È possibile creare un gruppo per tutti i tuoi dispositivi intelligenti e controllarli con un unico comando. Con la funzione di conto alla rovescia è sufficiente impostare la presa intelligente per spegnere automaticamente il dispositivo.\r\n? Controllo singolo e non, 3 prese WiFi per smartphone possono essere controllate separatamente o insieme. E 3 porte USB sono controllate come un\'unità. È possibile non solo accendere e spegnere i dispositivi elettrici collegati alla presa di corrente, ma anche spegnere tutti i dispositivi elettrici con un solo clic, quando si lascia o non si usa.\r\n⚡ Protezione da sovratensione e circuito dell\'aria: protezione da sovratensione e interruttore di protezione dell\'aria certificato sono un plus per proteggere i dispositivi elettronici e i dispositivi da sovracorrente e causare danni irreparabili. Cavo di alimentazione di alta qualità, molto spesso, con materiale ignifugo V0 e lunga durata. Carico massimo: 15 A.','111623509908502.jpg',28.95,23,3),(60,'Ciabatta Multipresa (10A/2500W) con 4 Prese Individuali e 4 USB (2,4A), per Smartphone,Casa,Ufficio','[Qualità superiore]: questo ciabatta multipresa con usb è realizzata in PC resistente alle alte temperature e materiali ABS, che elimina il pericolo nascosto di bruciature della presa. Inoltre, ha anche un cavo in rame standard da 1,5 mm² lungo 2 metri, robusto e durevole, molto adatto a qualsiasi luogo della casa o dell\'ufficio.\r\n[Presa di ricarica rapida USB]: costruito con tecnologia di ricarica intelligente, questo multipresa smart rileverà automaticamente i tuoi dispositivi e fornirà la velocità di ricarica più alta fino a 2,4 A, 4 porte USB ( 5 V / 3,4 A in totale) possono caricare fino a 4 dispositivi contemporaneamente, come smartphone, iPad, lettori MP3, Kindle, cuffie Bluetooth, luci di lettura, altoparlanti Bluetooth.\r\n[Protezione multipla]: La multipresa con interruttore protezione contro le sovratensioni fino a 900J per 8 uscite. In caso di cortocircuito o sovraccarico, l\'alimentazione verrà interrotta immediatamente per evitare danni all\'apparecchiatura. La porta di sicurezza impedisce l\'inserimento di oggetti estranei nella spina e tiene lontano da possibili rischi di scosse elettriche.\r\n[Nota importante]: la ciabatta elettrica multipresa è adatta per la maggior parte degli elettrodomestici in grado di supportare fino a 2500 W. Pertanto, si consiglia di verificare le esigenze di alimentazione prima dell\'uso, per garantire che la potenza totale non superi la potenza nominale.','121623509931202.jpg',16.14,20,3),(61,'Xiaomi Mi Air Purifier Pro','Purifica la tua casa in 12 minuti\r\nFiltra PM2.5, formaldeide e altre sostanze nocive\r\nSistema Dual Fan, 4 conduttori di aria dipendente\r\nCompatibilità: Android, IOS\r\nFunzione: modalità automatica / Sensore di luce / Display OLED / Modalità risparmio / Blocco bambini / Modalità preferita / Regolazione della velocità del vento','001623854705781.jpg',124.45,12,5),(62,'Xiaomi Mi Air Purifier 2H Purificatore d\'Aria, Rimozione Polline, Fumo, Polveri','Filtro HEPA originale per eliminare fino al 99,97% di fumo e particelle inquinanti\r\n260 m3/h di aria purificata e ampia copertura, 18-31m2\r\nControllo a distanza tramite MI Home App\r\nCompatibile con Amazon Alexa e Google Assistant\r\nSensori multipli per monitorare nello stesso momento: qualità dell’aria, temperatura e umidità\r\nSilenzioso, per un livello di rumore pari ad appena 66 DB','011623854726428.jpg',99.05,23,5),(63,'Proscenic A8 Purificatore d\'Aria, Filtro Ioni d\'Argento Antibatterico & Antiallergico','【Ampia Area di Utilizzo】: CADR fino a 220m³/h, il purificatore d\'aria Proscenic A8 isola efficacemente i fattori scatenanti delle allergie e il fumo,quindi perfetto per soggiorni, camere da letto, uffici, vivai e cucine ecc.Nota: Si raccomanda di sostituire il filtro ogni 3 mesi. (ASIN: B08BYQR8B8)\r\n【Pulificazione Profida】: Sistema di filtraggio 4 in 1 altamente efficace,cattura il 99,97% delle particelle fini e degli allergeni, fino a 0,3 micron, come polvere, forfora di animali domestici, fumo, muffe, polline. Combinato con PP, H13 HEPA, carbone attivo e ioni d\'argento, privo di ozono, non inquina, pulisce le sostanze inquinanti nell\'aria.\r\n【Rilevamento di Qualità dell\'Aria】: Luce della qualità dell\'aria con 3 livelli, è in grado di rilevare la qualità dell\'aria e di distinguere la qualità dell\'aria in quel momento con colori diversi. La luce verde è sinonimo di buona qualità dell\'aria, la luce rossa è sinonimo di pessima qualità dell\'aria, mentre la luce gialla è sinonimo di pessima qualità dell\'aria, che può farvi sentire il cambiamento della qualità dell\'aria.\r\n【Controllo Smart】:Controllo tramite app e touch screen, L\'operazione facile, il controllo di un pulsante, ha anche la luce notturna, se non ne avete bisogno, può essere spenta con un solo pulsante. Inoltre, ha anche la funzione di promemoria del cambio filtro, e visualizzare la qualità dell\'aria, Alexa, il controllo della casa di Google, molto comodo e intelligente.Nota: è supportato solo il WiFi a 2,4 GHz.','021623854757903.jpg',129.9,11,5),(64,'Philips Purificatore d\'Aria AC1215/10 con Tecnologia VitalShield IPS e Filtro Nanoprotect','Rimuove particelle ultrasottili fino a 0,02 µm (125 volte più piccole di PM2.5), il 99,9%* dei comuni allergeni e il 99,9% dei batteri**\r\nPermette ai soggetti allergici ai pollini di soffrire significativamente meno di sintomi come starnuti, lacrime, arrossamento degli occhi, prurito nasale, prurito e rinorrea***\r\nProtegge dall\'inquinamento, rimuovendo le particelle fino a 20 nanometri di dimensione (125 volte più piccole del particolato fine PM2.5)\r\nTecnologia VitaShield IPS e sistema a filtri multipli NanoProtect (pre-filtro, Carboni Attivi, HEPA), con cinque impostazioni di velocità (Silent, 1, 2, 3 e Turbo)\r\nSensore Intelligente AeraSense per il monitoraggio professionale della qualità dell\'aria dell\'ambiente domestico: dà un feedback luminoso LED e indica dell\'esatto livello di PM2.5 sul display digitale\r\nQuattro modalità per personalizzare la purificazione: Automatica, Batteri a maggiore potenza, Allergie per ridurre gli allergeni trasportati dall\'aria, Notturna con rumorosità e luminosità diminuite\r\nErogazione di Aria Pulita (CADR - Clean Air Delivery Rate) di 270m3/h, per coprire efficiaciemente superfici fino a 63m2','031623854785787.jpg',193.89,20,5),(65,'Bimar Purificatore d\'Aria Smart con WiFi e App, Elevata Purificazione','Caratteristiche Tecniche: Purificatore dotato di 3 filtri Filtro HEPA H13 Filtro a carbone attivo (lavabile) CADR (indice erogazione aria pulita): 125m³/h Display illuminato soft touch Ionizzatore (3mln. ioni negativi/cm³)\r\nGestione tramite APP Flusso aria regolabile in 3 velocità Spia per la sostituzione filtri Modalità Night e Standard Luce con effetti cromatici, o fissa Compatibile iOS e Android; funziona con reti domestiche Wi-Fi, 2.4GHz\r\nControllo vocale, compatibile con Amazon Alexa e Google Assistant e supporta IFTTT Ideale per ambienti di 20m² Dimensioni prodotto in (mm):215x460x215 Peso: 2,7 kg Accessori inclusi: Libretto delle istruzioni\r\nFUNZIONI - Design unico e moderno, è anche oggetto d\'arredamento. Il depuratore aria smart è destinato esclusivamente per purificare l’aria e sanificatore ambienti interni domestici o del vostro ufficio. Rimuove 99,97% di Allergeni, mangia Fumo, Polvere, Polline, Dander di Pet, forfora animali, Odore, Germi, Muffa, Senza ozono. Ionizzatore d\'aria (3mln. ioni negativi/cm³)\r\nINSTALLAZIONE - Utilizzare il purificatore aria per casa solo se il filtro e il coperchio sono assemblati in modo corretto. La presa di corrente deve essere facilmente accessibile in modo da poter disinserire con facilità la spina in caso di emergenza. Posizionare il purificatore per aria su superficie piana, orizzontale, e stabile. Assicurarsi che porte e finestre siano chiuse.','041623854818975.jpg',99.98,9,5),(66,'Robot Aspirapolvere Mini, 6D Sensore di Collisione WiFi/App/Alexa','【Tecnologia brevettata FreeMove 2.0 esclusiva】 Grazie alla nostra tecnologia FreeMove per il rilevamento dell\'atteggiamento a 360 ° di tutto il corpo, con sistema di giroscopio integrato e sensore di rilevamento del movimento, risolve efficacemente le difficoltà bloccate o intrappolate nel settore.\r\n【Design del corpo piccolo】 Design del corpo integrato, largo solo 11,02 pollici, progettato per lo spazio stretto tra il letto, il divano e l\'angolo del muro, evitare di chinarsi, ottenere un alto tasso di copertura e tasso di fallimento estremamente basso.\r\n【1500 Pa Super aspirazione】 Il design del motore digitale combinato con una forte aspirazione, usando la bocca di aspirazione senza spazzole, evita grovigli di peli di animali, non grippato, facile da pulire, basso consumo energetico e tempo della vita. Il contenitore della polvere da 500 ml riduce efficacemente la quantità di rifiuti, la filtrazione a doppio strato può prevenire l\'inquinamento secondario.\r\n【Batteria super potente】 Dotato di batteria al litio ferro fosfato 1800 mAh, carica e scarica fino a 5000 volte, durata, elevata sicurezza della batteria, supporta la ricarica automatica, può essere utilizzato a in qualsiasi momento!\r\n【Cosa ottieni】 Aspirapolvere robot Lefant-M201, 1 * Lefant Robot Vacuum M201,1 * Base di ricarica,1 * adattatore,1 * scatola di polvere,1 * spugna filtro,2 * pennelli,1 * Manuale dell\'utente e la nostra garanzia di 12 mesi senza preoccupazioni.','051623854847416.jpg',135.99,1,5),(67,'Aspirapolvere Robot, Aspirazione 2200Pa, Controllo WiFi','? Tecnologia innovativa con brevetto ?- SPOSTAMENTO GRATUITO: per evitare interruzioni quando l\'aspirapolvere robot è in funzione (è possibile rilevare ostacoli sotto il divano e le sedie, è anche possibile passare cavi elettronici e soglia più di 0,39 pollici, quasi totalmente automatico, senza necessità di intervento umano.)\r\n?Aspirazione potente e batteria durevole? - Il design sottile dell\'aspirapolvere robot è facile da pulire sotto il divano, il letto e gli angoli, con una forte aspirazione di 2200 PA per rimuovere polvere, sporco e peli di animali domestici . Il vuoto ha una batteria al litio da 2600 mAh che pulisce fino a 120 minuti con una singola carica, quando la batteria è scarica, tornerà automaticamente alla base di ricarica preparandosi per la pulizia successiva.\r\n?Un controllo Alexa e APP?- Pianifica la pulizia dalla tua mano con Alexa o Google Assistant o APP. Scarica l\'applicazione Lefant Life, puoi controllare l\'aspirapolvere intelligente per varie funzioni come la programmazione della pulizia, la modifica delle modalità di pulizia, la regolazione della potenza di aspirazione, ecc. È inoltre possibile programmare l\'aspirapolvere con il telecomando LCD. Inizia e programma la pulizia quando e dove vuoi!','061623854868412.jpg',141.98,23,5),(68,'Uoni V980Plus Robot Aspirapolvere Lavapavimenti Raccolta Automatica Della Polvere con Mappatura','【Controllo Vocale- Vita Intelligente】Ti offre una vita confortevole e intelligente, puoi facilmente controllare il robot automatico dell\'aspirapolvere semplicemente aprendo la bocca. Uoni funziona con dispositivi di controllo vocale di Alexa e Google Assistant, si è in grado di iniziare a pulire con la voce. E anche si potrebbe pulire la vostra casa semplicemente utilizzando Uoni Robot APP, telecomando o semplicemente premere il pulsante del robot.\r\n【Tecnologia di Mappatura in Tempo Reale】Robot aspirapolvere con tecnologia di mappatura Lidar, può ripristinare il tuo modello di casa nel modo più accurato possibile, rilevare oggetti fissi, analizzare ostacoli dinamici e fare rapidamente previsioni ed evitarli. 25 sensori avanzati e lidar consentono al pulitore robot Uoni di navigare sotto e intorno ai mobili, e lungo i bordi. I sensori scogliera rilevano impedisce che cada giù per le scale.\r\n【 Pulizia personalizzata】l\'app Uoni Robot aggiornerà la mappa in tempo reale che gli aspirapolvere robot puliranno la tua casa in base alla stanza o all\'ordine delle tue istruzioni come un essere umano. L\'App ti permetterà di gestire in modo intelligente il tuo piano di pulizia come aggiungere pareti virtuali e zone No-go, impostare orari di pulizia, pulizia zona e \"trovare\" il tuo robot.','071623854896533.jpg',699.99,6,5),(69,'Dreame D9 Robot Aspirapolvere, 2 in 1, Aspirazione 3000 Pa, Alexa, Wi-Fi','Navigazione LiDar: l’avanzato sistema di navigazione LiDar permette una migliore accuratezza, una mappatura più veloce, prestazioni più potenti nell’aggiramento degli ostacoli e una pianificazione del percorso più efficiente. Con la navigazione SLAM, il robot D9 apprende la distribuzione degli spazi in casa e crea mappe intelligenti per una pulizia lineare sistematica ed efficiente.\r\nPrestazioni potenti: i 3000 Pa di aspirazione intensa raccolgono facilmente lo sporco e la polvere dalle superfici e nei tappeti. La potente aspirazione del robot aspirapolvere D9 può rimuovere efficacemente capelli, polvere e altre particelle.\r\nTanti modi per pulire: il robot offre diversi metodi di pulizia; una stanza alla volta fino a coprire l’intera casa, accesso limitato per evitare zone come l’area gioco dei bambini, e pulizia doppia per un risultato impeccabile.\r\nLavaspira 2-in-1: il robot seleziona tra le modalità di aspirazione o lavaggio. La batteria ad alta capacità da 5200 mAh offre fino a 150 minuti di operatività: quanto basta per pulire circa 200 m² a bassa energia con un’unica carica.\r\nControllo intelligente con l’app: il controllo remoto dall’app permette di regolare la potenza di aspirazione e la distribuzione dell’acqua, e di verificare il livello della batteria. Quando connesso ad Alexa, il robot risponde immediatamente ai comandi vocali.\r\nNota: per il robot aspirapolvere D9 offriamo 1 anno di garanzia.','081623855213031.jpg',299.23,11,5),(70,'Xiaomi 25012, Vacuum-Mop, Robot Aspira e Lava, vSLAM Mappatura in Tempo Reale','Navigazione visiva dinamica, dotata di un sistema di navigazione visiva ad alta velocità per aiutare a catturare rapidamente informazioni coordinate, estrarre complesse funzioni spaziali e creare mappe più rapide e accurate.\r\nDotato di un serbatoio di acqua intelligente a controllo elettronico da 200 ml, l\'acqua viene controllata automaticamente durante tutto il processo, trascinamento più uniformemente bagnato, scopa sospesa vicino al terreno.\r\nLa fusoliera è dotata di 15 tipi di sensori ad alta precisione, che hanno una maggiore capacità di adattamento all\'ambiente, il che aiuta a eseguire azioni migliori come anticaduta, infrarosso, anticollisione, rilievo ed evitamento di ostacoli.\r\nRicaricare automaticamente al di sotto del 15%, quando la batteria viene caricata all\'80%, tornerà automaticamente al punto di interruzione per continuare la scansione.\r\nCon l\'app Mi Home,Corpo sottile 8,2 cm, camminando più liberamente a casa, altezza ostacolo in salita di 20 mm.\r\nComponenti inclusi: robot aspirapolvere, stazione di ricarica, spazzola principale e laterale, spazzola per la pulizia, salviette, contenitore per la polvere, serbatoio dell\'acqua, cavo di alimentazione, manuale utente (lingua italiana non garantita).','091623855241919.jpg',193.26,45,5),(71,'Moulinex HF900110 i-Companion Robot Multifunzione da Cucina, Connesso alla sua App Dedicata','Collegamento con la sua app\r\n4 programmi + modalità manuale\r\n21 impostazioni di temperatura\r\nRicettario cartaceo non incluso, le ricette si scaricano via app.\r\nTrita, prepara, impasta, cuoce, soffrigge e cucina al vapore','101623855270948.jpg',491.34,5,5),(72,'Cecotec Robot da Cucina 10090. Con App cucchiaio MamboMix, brocca Habana, 30 funzioni','Robot da cucina multifunzione con 30 funzioni: tagliare a pezzi, sminuzzare, centrifugare, triturare, soffriggere, macinare, polverizzare, grattugiare, riscaldare, frullare, funzione yogurt, montare, emulsionare, mescolare, cucinare, girare, cucinare al vapore, sobbollire, confit, impastare, cucinare a bassa temperatura, bollire, mantenere caldo, fermentare, SlowMambo, cucinare con precisione grado per grado, cucinare a bagno maria, cottura lenta, velocità zero e funzione Turbo.\r\nApp Mambo per Smartphone con ricette illimitate guidate passo a passo, modalità predefinite per rendere facile il suo uso e sistema manuale DIY.\r\nCaraffa in acciaio inossidabile lavabile in lavastoviglie. Caraffa indipendente in acciaio inossidabile con rivestimento in ceramica altamente antiaderente con risultati eccellenti per i piatti più delicati. La caraffa ha una capacità massima di 3,3 litri. Cestello per bollire per poter preparare fino a 4 piatti allo stesso tempo. Cucinare nella caraffa, nel cestello e nella vaporiera a due livelli è ideale per risparmiare tempo in cucina.\r\nLa sua velocità 0 permette di cucinare e soffriggere senza necessità di inserire la velocità come se si trattasse di una casseruola o di una padella. Movimento SlowMambo che permette di cucinare a bassa temperatura mentre gira costantemente con il cucchiaio MamboMix in modo tradizionale.','111623855300947.jpg',349.76,9,5),(73,'Kenwood CCL401WH kCook Multi Smart Robot da Cucina Food Processor con Funzione Cottura','Robot da cucina con processore di alimenti e ricettario; 1500 W in fase di cottura, 550 W per lavorazioni a freddo\r\n2 postazioni di preparazione indipendenti e la funzione cottura, capacità ciotola 4.5 litri, consente di preparare fino a 8 porzioni\r\n6 programmi pre-impostati, 12 velocità pre-impostate di mescolamento, temperatura regolabile tra 30° C - 180° C\r\nL\'accessorio Direct Prep permette di affettare e grattugiare direttamente nella ciotola durante la cottura\r\nSi può impostare anche il tempo fino a 8 ore di cottura\r\nIn dotazione: cestello per la cottura a vapore da 7.2 litri, lama in acciaio inox, frusta, pala mescolatrice, adattatore per la cottura lenta, 5 dischi per tagliare e grattugiare','121623855323230.jpg',505.16,17,5),(74,'Decdeal Termostato Wi-Fi per Caldaia a Gas, Touchscreen con Retroilluminazione, Controllo Vocale','♥【Termostato di Alta Qualità 】Precisione di 0.5 ° C, mantenendo la temperatura confortevole all\'interno del livello impostato ( Intervallo: 5 ~ 35 ° C ).con pulsanti a sfioramento per un funzionamento semplice. per impianto di riscaldamento con caldaia a gas.\r\n♥【 Display LCD con Retroilluminazione 】Visualizzare i dati in modo chiaro e facile da leggere anche al buio.Funzione di memoria dati quando l\'alimentazione è spenta; funzione di blocco della tastiera.\r\n♥【Supporto APP】Controllo remoto dell\'App comodo per controllare il termostato con il tuo dispositivo mobile. Crea gruppo termostato per controllo centralizzato.\r\n♥【Programmabile】Settimanalmente e 5 + 2 sei periodi programmabili per massimizzare comfort ed economia, molto flessibile.\r\n♥【Controllo Vocale】Compatibile con Amazon Echo, Alexa, Google Home, IFTTT.','001623855415265.jpg',39.09,23,4),(75,'OWSOO Sensore per Porte Senza fili Rilevatore di Intrusione per Porte WiFi Allarme di Sicurezza','✿【Telecomando APP】Scarica l\'app TUYA dall\'App Store per aggiungere il dispositivo per rilevare lo stato della tua porta / finestra. Sicurezza domestica sempre in mano.\r\n✿【Facile da installare】 Basta attaccare un pezzo alla porta o alla finestra e attaccare l\'altro pezzo al telaio della porta o della finestra. Alimentato a batteria, comodo per sostituire la batteria e facile da installare con adesivi.\r\n✿【Usa lo scenario】Avviso di intrusione ideale per case, appartamenti, case mobili, dormitori, uffici, camere d\'albergo, garage, ecc.\r\n✿【Design a basso consumo】 che può farlo funzionare a lungo. Design semplice con dimensioni compatte, elegante e alla moda.\r\n✿【condividere il controllo】Questo sensore deve funzionare con Amazon Alexa, Google Assistant, IFTTT o Rokid.','011623855449464.jpg',14.99,11,4),(76,'Sensore di movimento wireless WiFi Smart Home PIR rilevatore di movimento','【Fai da te】: facile da installare. Nastro biadesivo 3M (incluso) può essere utilizzato per fissare il sensore.\r\nMonitoraggio professionale 24 ore su 24: il sensore PIR intelligente offre un monitoraggio continuo della tua casa. Da utilizzare in una stanza o ovunque si voglia ricevere una notifica quando si muove o si muove. Cattura ogni attività e invia avviso al tuo telefono Android o Apple. È facile per voi tenere d\'occhio la sicurezza della vostra casa in qualsiasi momento e ovunque.\r\nAvvisi rapidi: quando viene attivato, come movimento umano o movimento, una notifica istantanea verrà segnalata al tuo smartphone entro 3-10 secondi dipende dalla velocità di Internet. La funzione di ricomposizione integrata consente di collegare automaticamente il sensore PIR alla rete dopo aver ripristinato il router wifi.\r\nFunziona con altri prodotti intelligenti AIIAT: insieme alla presa intelligente AIIAT e puoi creare le tue scene come la presa o l\'elettrodomestico possono essere attivato/disattivati automaticamente quando vengono rilevati movimenti o movimenti.\r\nInterruttore a due modalità: è possibile selezionare due modalità per questo sensore. Modalità 2 e 4 minuti. Il sensore rileva immediatamente il primo movimento e poi attende 2 o 4 minuti prima di rilevare il movimento successivo. Ciò eviterà il falso allarme e salverà anche la durata della batteria. Puoi scegliere quello che ti serve','021623855478511.jpg',21.89,2,4),(77,'Panamalar Sensore per porte e finestre WiFi,rilevamento dello stato della porta, avviso telefono','?[INIZIA IL TUO SMART LIFESTYLE]- Vuoi che le luci della casa si accendano automaticamente una volta entrati dalla porta principale? Basta integrare il sensore con le tue luci intelligenti! Crea una scena intelligente sull\'APP Smart Life, la luce può essere accesa automaticamente quando apri la porta.\r\n?[SENSORE MULTIUSO DENTRO LA TUA CASA]- Il sensore del finestrino si attiverà quando la porta o la finestra viene aperta o chiusa, riceverai una notifica istantanea e un allarme sul telefono entro circa 2-8 secondi dipende dalle reti Wifi.(NOTA: supporta solo 2,4 Ghz. Nessuna sirena incorporata, ma il messaggio di avviso verrà inviato al telefono.)\r\n?[MONITORAGGIO 24/7]- La funzione di notifiche push può essere disabilitata sull\'app quando sei a casa e il sensore della porta wifi continuerà a registrare la cronologia in modo che tu possa controllarla quando ne hai bisogno.\r\n?[FACILE INSTALLAZIONE]- Molto facile da installare tramite i nastri o le viti montate sul telaio della porta o della finestra. Nastri e viti inclusi, tutto ciò che serve per iniziare è nella confezione. NOTA: assicurarsi che il sensore e il magnete siano allineati e con uno spazio inferiore a 10 mm.\r\n?[ALEXA & GOOGLE HOME]- Chiedendo a Alexa o Google home, puoi conoscere facilmente lo stato delle porte. Montare su porte anteriori, porte posteriori, porte da garage, finestre, armadi, cassetti o ovunque tu voglia sapere se è aperto.','031623855504724.jpg',18.96,34,4),(78,'Tuya Wifi Smart Temperature Monitor Sensor - WiFi Gas Alarm Detector Alarm Home Use','[Gas rilevabile]: il rilevatore di fughe di gas naturale rileva i principali gas mortali: gas naturale (metano, butano, etano, GNL, ecc.) E propano (GPL o gas di petrolio liquefatto).\r\n[Allarme in loco e allarme remoto APP]: questo allarme gas può essere collegato allAPP del telefono cellulare in un ambiente wireless. Dopo la connessione, puoi sempre ricevere le informazioni dellallarme quando esci e una volta che lallarme si è verificato.\r\n[Alta precisione]: ha uno schermo LCD che visualizza letture di gas precise e in tempo reale in modo da conoscere in ogni momento lesatta concentrazione di gas nellaria di casa.\r\n[Posizione di installazione]: questo allarme per propano ha un cavo di alimentazione lungo 6 piedi che consente di installarlo nella posizione ideale esatta in casa per consentire il rilevamento corretto del gas. Fissare a vite o incollare linstallazione.\r\n[Aspetto elegante]: questo nuovo allarme per gas naturale è elegante e moderno e ha un bellissimo schermo LCD blu che completerà lo stile della tua casa o del tuo camper senza nulla togliere al design degli interni.','041623855539037.jpg',26.51,15,4),(79,'Sensore di inondazione TELLUR WiFi Smart, App per Smartphone, Allarme Acustico e notifica Push','Allarme acustico e notifiche al telefono quando viene rilevata una perdita d\'acqua\r\nFacile da installare e utilizzare con TELLUR Smart app, Smart Life app, tuya app (iOS e Android); uso indipendente tramite internet, solo con smartphone; nessun hub richiesto; supporta fino a 150 dispositivi registrati\r\nTimer; programma; scenari; funzioni integrate app completamente personalizzabile\r\nFunziona con batterie AAA classiche, con basso consumo energetico e notifica di batteria scarica; più di 6 mesi dipende da quanto spesso ricevi notifiche\r\nNOTA: Richiede una connessione di rete Wi-Fi a 2,4 GHz','051623855562761.jpg',19.98,21,4),(80,'meross Sensore di Umidità e Temperatura Wireless, Termometro e Igrometro','Smart Hub V4: meross sensore umidità e temperatura funziona con l\'hub di versione V4, un hub serve per 16 sensore, non bisogna ogni volta prendere il kit sensore +hub, ma solo un sensore perchè con un hub ne comandi fino a 16! Se hai un hub versione V2 (2.0.0), ti preghiamo di contattarci. Meross ti offre un servizio di upgrade.\r\nControlla i dati da qualsiasi posto e qualsiasi momento: attraverso l\'applicazione meross (iOS o Andriod), puoi controllare la temperatura e l\'umidità ovunque e in diversi momenti. Tutti i dati vengono trasmessi in modo sicuro da Meross Sensor (HTTPs e TLS). Tutti i dati storici di temperatura e umidità possono essere archiviati nel cloud Amazon AWS. Puoi esportarli tramite l\'app gratuita Meross.\r\nGamma potente: tra il sensore di temperatura e umidità e lo Smart Hub, la distanza è di 30 metri tra due pareti. La gamma di temperatura tipico tra -10 e 40 ° C (troppo freddo o troppo caldo riduce la durata della batteria). La gamma di umidità tra 0 e 100% RH.\r\nPiccolo ma Preciso: dimensioni più piccole, peso più leggero. I componenti del sensore sono stati fabbricati in Svizzera e sono misurazioni accurate. La batteria CR2477 di solito funziona per oltre un anno in condizioni normali. Il sensore wireless fornisce un filo di sospensione e il nastro biadesivo da 3M.\r\nAllarme e Automazione: quando la temperatura o l\'umidità arriva al valore particolare, il sensore Meross invia immediatamente avvisi allo smartphone.','061623855586328.jpg',23.99,9,4),(81,'Govee Termometro Igrometro, accurato Misuratore Interna con Intelligenti avvisi App e Dati','Display LCD chiaro e numeri grandi: Offre letture di temperatura, umidità in tempo reale e registrazioni max e min sullo schermo LCD. Indicatori di comfort a 3 livelli: asciutto, comfort, umido.\r\nAlta precisione: La temperatura è precisa fino a ± 0,5 ° F mentre l\'umidità è ± 3% RH. La velocità di risposta fino a 2 secondi mostra sempre le ultime letture.\r\nPortata remota fino a 60m: La funzione bluetooth consente monitoraggio remoto di temperatura e umidità sull\'App Govee Home. Il può in cameretta, asilo nido, cantina, seminterrato, magazzino e serra.\r\nAvviso di notifica intelligente: Una notifica di avviso verrà inviata al telefono quando la temperatura o l\'umidità non rientrano nell\'intervallo preimpostato.\r\nArchiviazione dati gratuita: Offre una memorizzazione dei dati a bordo di 20 giorni.Puoi esportare i dati degli ultimi 2 mesi in formato CSV. Alimentato da 2 batteria AAA (inclusa).','071623855613417.jpg',15.99,13,4),(82,'Brennenstuhl 1294360 BrematicPRO Senza Fili, sensore di luminosità Smart Home per Interni ed Esterni','Il sensore di luce wireless rileva la luminosità attuale in interni o esterni, come ad esempio in camera da letto o davanti all\'ingresso di casa\r\nIl sensore di luminosità radio rileva un intervallo di misurazione di circa 5 minuti di luminosità ambientale (in Lux)\r\nIl sensore di luce indica il valore di luminosità rilevato (luminosità viene emessa in Lux) nel punto di installazione al BrematicPRO Gateway (l\'uso dell\'app BrematicPRO per controllare gli attuatori e i sensori è possibile solo in combinazione con il BrematicPRO Gateway)\r\nLettura dei valori di luminosità e attivazione di azioni dovute alla luminosità (ad esempio Chiusura automatica delle tapparelle in caso di luce solare eccessiva) comodamente tramite app\r\nContenuto della confezione: 1 sensore di luminosità radio 868.3 MHz, 2 batterie tipo AA/LR6/Mignon, 1 piastra di montaggio, 1 set di montaggio (viti/tasselli) – nella migliore qualità di Brennenstuhl','081623855633667.jpg',35.86,6,4),(83,'Decdeal Termostato WiFi per Caldaia, Controllo App Retroilluminato Visualizzazione dell\'umidità e UV','✅【Termostato Programmabile】5 + 1 + 1 Modalità di programmazione ogni settimana. Nei giorni feriali (dal lunedì al venerdì), sabato, domenica, in diversi momenti della giornata, è possibile impostare la temperatura separatamente per una facile programmazione. Supporta la condivisione dei dispositivi. La memoria dati incorporata può memorizzare i dati di programmazione senza preoccuparsi di interruzioni di corrente.\r\n✅【Controllo APP】Termostato Smart WiFi, scarica l\'APP nell\'app store mobile: My Smart Life (la lingua dell\'interfaccia dell\'APP può essere impostata su italiano e altre lingue europee), anche se sei all\'aperto, puoi anche controllare a distanza il termostato della tua casa dal tuo cellulare.\r\n✅【Controllo Vocale】Il termostato è compatibile con Alexa, Amazon Echo, Google Home e Tmall Genie. Scansionando il codice QR qui sotto, puoi ottenere un manuale multilingue come l\'italiano, che ti introdurrà al processo operativo in dettaglio.Se hai domande, non esitare a contattarci.\r\n✅【Schermo LCD Moderno】LCD retroilluminato a colori di grandi dimensioni da 4.8 pollici, facile da leggere di notte. Il touch screen semplifica le operazioni. Lo spessore visibile sopra il muro è di soli 15 mm. Tenere premuto il pulsante per 5 secondi per bloccare / sbloccare il termostato, è possibile scegliere di bloccare completamente o parzialmente il blocco.','091623855666042.jpg',71.99,99,4),(84,'Bticino Termostato WiFi intelligente Smarther2 with Netatmo SXW8002, Incasso, Bianco','Versione 2.0 dell\'esclusivo termostato connesso di Bticino\r\nTipologia: da incasso, all\'interno del muro\r\nGrazie al WiFi integrato, è connesso e gestibile da smartphone, anche da remoto\r\nComandabile con controllo manuale, assistente vocale Alexa, Google Home o Apple HomeKit, oppure applicazione Home + Control, ovvero l\'unica app in cui configurare tutti i dispositivi connessi Bticino della gamma \"with Netatmo\"\r\nCompatibile con gli impianti di riscaldamento autonomi e centralizzati con termovalvole. Non compatibile con gli impianti di riscaldamento centralizzati senza valvole\r\nRiscalda subito e con rapidità la tua casa grazie alla funzione BOOST\r\nATTENZIONE! Verifica se Smarther2 è compatibile con il tuo impianto sul sito ufficiale del produttore prima di acquistarlo!','101623855686882.jpg',179.99,21,4),(85,'Bticino Termostato WiFi intelligente Smarther2 with Netatmo SXW8002W, da Parete, Bianco','Versione 2.0 dell\'esclusivo termostato connesso di Bticino\r\nSi applica direttamente a muro, sulla parete\r\nGrazie al WiFi integrato, è connesso e gestibile da smartphone, anche da remoto\r\nComandabile con controllo manuale, assistente vocale Alexa, Google Home o Apple HomeKit, oppure applicazione Home + Control, ovvero l\'unica app in cui configurare tutti i dispositivi connessi Bticino della gamma \"with Netatmo\"\r\nCompatibile con gli impianti di riscaldamento autonomi e centralizzati con termovalvole. Non compatibile con gli impianti di riscaldamento centralizzati senza valvole\r\nRiscalda subito e con rapidità la tua casa grazie alla funzione BOOST\r\nColore: bianco\r\nTipologia: da muro/parete','111623855704451.jpg',179.99,15,4),(86,'Xiaomi Mi Temperature and Humidity Monitor 2','Monitora la temperatura e l\'umidità in tempo reale\r\nLa connessione Bluetooth consente l\'accesso remoto tramite smartphone\r\nPrecisione della temperatura di 0,1 ° C e precisione dell\'umidità dello 0,1%\r\nDimensioni: 3.60 x 3.60 x 1.15 cm\r\nPeso: 15 g','121623855719875.jpg',9.99,34,4),(87,'Xiaomi Mi 360° Home Security Camera 2K Pro','La risoluzione 2K ad alta definizione offre immagini più naturali. Visione a 360 gradi\r\nIl doppio microfono per l\'audio bidirezionale offre un\'esperienza di chiamata HD\r\nIl riconoscimento facciale AI è facile da identificare i tuoi amici e parenti\r\nFabbricato in Cina','001623924176953.jpg',46.15,32,6),(88,'Serratura Intelligente Biometrica, Smart Door Lock, con impronta digitale, controllo APP','【Sistema avanzato di impronte digitali】 Ottieni il set completo di sistemi di sicurezza per porte per aggiornare la tua sicurezza domestica. Utilizzando un sensore di impronte digitali di contatto avanzato, la velocità di riconoscimento può raggiungere 5 ms, un basso tasso di falso riconoscimento.\r\n【Controllo APP】 L\'utente si sblocca tramite l\'impronta digitale e l\'APP o la password e l\'amministratore può trovare il record nel record di sblocco sull\'APP. Controllare i punti di accesso per la tua casa o per il tuo lavoro non è mai stato così facile o intelligente.\r\n【Antifurto】 Maniglia password nascosta, può impostare una password dinamica temporanea. Se è necessario lo sblocco remoto, è possibile impostare una o più password dinamiche. Dopo lo sblocco, la password diventa automaticamente non valida.\r\n【Anti-pigolio】 Quando viene inserita 5 volte la password errata, la serratura intelligente si blocca automaticamente per 5 minuti; è possibile immettere un numero qualsiasi di password prima della password corretta e solo le ultime cifre sono la password corretta per sbloccare.\r\n【Basso consumo energetico】 Chip a basso consumo energetico integrato, elimina il problema di cambiare la batteria, lunga durata della batteria. Accesso fino a 8.000 volte.','011623924202903.jpg',72.34,10,6),(89,'Telecamera wi-fi interno smart eufy 2K, con riconoscimento persone/animali, visione notturna','Riconosce chi passa: la tecnologia AI integrata nel dispositivo stabilisce se sia una persona o un animale domestico a passare nel campo visivo della telecamera (solo riconoscimento del volto umano).\r\nLa soluzione è nel dettaglio: visualizza ogni evento con una nitidezza da 2K, in modo da farti sapere esattamente cosa avviene all’interno della tua abitazione ( (1080P durante l\'utilizzo di HomeKit).\r\nIntegrazione intelligente: collega la telecamera per interni ad Apple HomeKit, Assistente Google o Amazon Alexa per un controllo completo sulla videosorveglianza. (HomeKit disponibile tramite aggiornamento. All\'apertura di HomeKit, gli utenti devono aggiungere il dispositivo nell\'app di eufy security e quindi completare il processo di attivazione.)\r\nMonitora i movimenti: in caso di rilevazione di un movimento, la telecamera segue e monitora l\'oggetto automaticamente. Sposta l’obiettivo di 360° in orizzontale o inclinalo di 96° in verticale per avere una visione chiara dell’intera stanza. \r\nComunica attraverso la telecamera: parla in tempo reale con chiunque passi davanti all’obiettivo utilizzando l’audio bidirezionale integrato nella telecamera.','021623924222215.jpg',38.99,12,6),(90,'Netvue Telecamera Wifi Esterno 1080P Videocamera Sorveglianza con Rilevamento di Umano Movimento','【AI Intelligente. Rilevazione Umana】L\'algoritmo avanzato opzionale è in grado di riconoscere il corpo umano da altri movimenti, eliminando i falsi allarmi innescati da insetti volanti, animali domestici e luci e inviando immediatamente notifiche al telefono, permettendoti di concentrarti su ciò che conta. Non solo è possibile ottenere il rilevamento umano o tutte le notifiche di movimento, ma è anche possibile impostare la sensibilità, il tempo e l\'area di rilevamento del movimento.\r\n【Full HD 1080P e Visione Notturna】 La telecamera wifi da esterno Netvue può acquisire immagini e video super 1080P anche durante il giorno e la notte. Il potente LED a infrarossi 3X850NM consente di ottenere una visione chiara al buio, la distanza di visualizzazione sarà fino a 60 piedi (30 m).\r\n【COMPATIBILE CON SMART HOME - Funziona con ALEXA】 Questa telecamera wifi esterno può essere controllata da Alexa tramite Echo Show, Echo Spot, Echo Plus o Fire TV ecc. Con l\'assistenza vocale, può essere più conveniente per monitorare la tua casa ovunque e in qualsiasi momento .\r\n【Impermeabilità IP66】La telecamera sorveglianza wifi esterno Netvue ha una straordinaria capacità di resistere all\'acqua, supportare la gamma di ambienti da -20° C a 50° C da -4° F a 12°F. La Vigil Cam è abbastanza forte per resistere estreme meteorologiche e danni. È più adatto per esterni, come cortile, corridoio, porta, parcheggio, giardino, vano scala, ecc.','031623924247306.jpg',26.54,7,6),(91,'Xiaomi Mi Kit di Sicurezza, Automazione Domotica Casa, Compatibile con dispisitivi Mi Home','Mi Control Hub: centro di controllo della tua casa smart\r\nInstallazione semplice\r\nRileva i movimenti umani e degli animali\r\nCampanello di allarme per porte e finestre\r\nMini telecomando di controlli degli oggetti collegati\r\nCompatibile con sistemi e dispositivi Mi Home\r\nTemperatura di funzionamento: -5°C - +40°C','041623924266842.jpg',78.87,44,6),(92,'EZVIZ LC1C Telecamera Wi-Fi di Sorveglianza con Luce per Esterno 1080p, Faretti LED 2000 Lumen','Sensore PIR ad Alta Sensibilità: Con un sensore PIR a 270 °, la telecamera di sorveglianza LC1C rileva movimenti umani e animali fino a 25 metri; trasmetti video mozzafiato a 1080p sui tuoi dispositivi mobili per la protezione 24 ore su 24, 7 giorni su 7; puoi persino configurare le zone in cui desideri abilitare l\'illuminazione con movimento PIR\r\nRilevazione delle Intrusioni: La telecamera EZVIZ LC1C ha un doppio sistema di rilevazione per aumentare la precisione degli avvisi; combinando le tradizionali analisi di rilevamento del movimento con un sensore PIR, LC1C garantisce di ricevere solo notifiche degne di attenzione; inoltre, è possibile attivare la sirena da 100 decibel e la luce lampeggiante per scoraggiare gli intrusi\r\nIlluminazione Intelligente: La telecamera EZVIZ LC1C è un sostituta ottima per le luci da giardino; progettata pensando alla flessibilità, LC1C può essere ruotata per orientarsi in qualsiasi direzione desiderata; puoi usarla come una luce da patio convenzionale, accendere manualmente i riflettori e regolare la luminosità utilizzando l\'app EZVIZ\r\nAccesso Remoto Bidirezionale: La funzione di chiamata bidirezionale aiuta ad ascoltare e parlare con i visitatori e persino a bloccare gli intrusi in tempo reale; quando sei al lavoro o quando arriva il fattorino puoi accedere al video live tramite WiFi nell\'app EZVIZ e parlare con il servizio di messaggistica o suonare la sirena se qualcosa non va','051623924295765.jpg',124.45,8,6),(93,'Xiaomi Mi Home Security Camera 360° 1080P, Visione notturna, Connettività Wi-Fi','Video-registrazioni a 1080p\r\nRuota a 360° per una piena visibilità\r\nVisione notturna potenziata\r\nCollegamento a due vie\r\nAlerts istantanei con person detection avanzata\r\nL\'articolo non viene fornito con la spina, solo con USB','061623924316806.jpg',29.99,20,6),(94,'eufy Security, videocitofono wireless (batteria) 2K HD, tecnologia AI integrata, audio a 2 vie','Nitidezza di 2,5 volte migliore: il sensore 2K Sony integrato e l’obiettivo di livello professionale ti permettono di visualizzare le attività all’aperto con una risoluzione perfetta delle immagini.\r\nNessun costo mensile: progettati per salvaguardare l’abitazione e contenere i costi, i prodotti eufy Security non implicano l’acquisto di ulteriori parti e combinano sicurezza e convenienza. Tutti i dati vengono memorizzati a livello locale.\r\nOpzioni di doppia alimentazione: sei mesi di copertura grazie a una sola ricarica oppure un’alimentazione ininterrotta grazie a una connessione cablata.\r\nCampo visivo esteso: l’aumento del rapporto di aspetto 4:3 ti assicura una vista a figura intera di chiunque si avvicini e ti garantisce un angolo visivo sempre perfetto.\r\nRilevamento di livello superiore: avrai il il controllo completo su ciò che viene rilevato grazie all’uso del sensore di movimento, del rilevamento persone intelligente e delle zone di attività. Ricevi gli avvisi in tempo reale.','071623924340111.jpg',169.99,25,6),(95,'YI Home Camera 1080p Kit da 2 ,IP Camera WiFi, con Rilevamento di Movimento, Audio Bidirezionale','RISOLUZIONE IN FULL HD CON VISIONE NOTTURNA NON INVASIVA: Filmati in live-streaming e video in 1080P / 20fps - Lenti grandangolo da 110° con zoom digitale 4x - 8 LED infrarossi non visibili al buio per eccellente visione notturna non invasiva - Luce LED di stato disattivabile via app YI Home (ideale per l’utilizzo durante il sonno).\r\nRILEVAMENTO DI ATTIVITÀ SMART SUPPORTATO DA INTELLIGENZA ARTIFICIALE - RILEVAMENTO UMANO: rileva persone in movimento fino a 20 fps - RILEVAMENTO SONORO: rileva suoni tra 50 e 90+ dB (regolabile) - NOTIFICHE & VIDEO DELL’ATTIVITÀ RILEVATA: Notifiche push su smartphone (frequenza e livello di sensibilità regolabili) e registrazione video di 6 secondi ad ogni attività rilevata dalla telecamera - DELIMITAZIONE AREA ATTIVITÀ (OPZIONALE).\r\nDESIGN TASCABILE, ELEGANTE E VERSATILE: Telecamera di piccole dimensioni (8 x 12 cm x 3 cm / 120g) con robusto supporto inclinabile e lente estraibile - ON / OFF PROGRAMMABILE: Accensione e spegnimento della telecamera programmabile per ogni giorno della settimana via app YI Home - SICUREZZA AVANZATA: Codice PIN opzionale per accedere al livestream e alle impostazioni della telecamera wifi.','081623924382060.jpg',48.99,48,6),(96,'Google Nest Cam Indoor Nero, Sicurezza senzainterruzioni, Sul tuo smartphone 24/7','Configurazione semplice e veloce, per iniziare, collega Nest Cam e scarica l\'app Nest, non è necessario un hub\r\nVideo in diretta 24/7, angolo di visione di 130° che mostra la tua casa in HD a 1080p, anche al buio\r\nAvvisi direttamente sul tuo smartphone, ricevi avvisi di attività in modo da essere sempre al corrente se succede qualcosa quando non sei in casa\r\nVisione chiara come il sole, anche al buio, la funzione Visione notturna illumina uniformemente tutta la stanza\r\nParla e ascolta, Vedi se c’è qualcuno e parla per attirare la sua attenzione, Per alcune funzionalità, tra cui notifiche per dispositivi mobili, controllo remoto, streaming video e registrazione video, è necessario che Internet e la connessione Wi-Fi funzionino correttamente','091623924421594.jpg',177.11,11,6),(97,'Teckin Telecamera Wi-Fi Interno, 1080P Videocamera di Sorveglianza, Multi-angolo','Facile da usare: TECKIN CAM è facile da usare lavorando su una rete Wi-Fi a 2,4 GHz con configurazione tramite la nuova App Teckin. Installabile ovunque come pareti o soffitti. Goditi lo streaming live ad alta definizione con 1080P e obiettivo grandangolare da 110 gradi.\r\nRilevamento di movimento / suono: TECKIN CAM salva automaticamente qualsiasi rilevamento di movimento e suono. L\'app Teckin invierà notifiche al tuo telefono o tablet per garantire che la tua casa sia al sicuro anche quando sei lontano.\r\nAudio bidirezionale e visione notturna: TECKIN CAM offre conversazioni video e audio in tempo reale che ti consentono di parlare direttamente con la tua famiglia e persino con il tuo amato animale domestico. La modalità Night Vision ti consente di vedere chiaramente al buio. Con una portata di 32 piedi con 6 LED IR, TECKIN CAM è perfetta per l\'utilizzo di notte quando sei fuori o anche solo a letto!\r\nCondividi con la famiglia: TECKIN CAM può condividere lo streaming live con chiunque condividi l\'app TECKIN. Familiari, amici e persone care possono accedere e parlare con chiunque in casa, fino al tuo animale domestico!\r\nServizio cloud e archiviazione SD: TECKIN CAM fornisce sia archiviazione T-Cloud che scheda SD (max 128 GB) Ciò consente di registrare il video e l\'audio in due posizioni di archiviazione. TECKIN offre molti pacchetti preferenziali attraverso il nostro T-cloud che possono soddisfare le tue esigenze.','101623924457687.jpg',18.69,34,6),(98,'Google Nest Hello','L\'unico campanello davvero intelligente:\r\nNest Hello può rilevare la differenza tra una persona e un oggetto e inviarti un avviso.\r\nSaluta anche quando non puoi:\r\nLa funzionalità Parla e ascolta in HD riduce i rumori ambientali e ti consente di sentire i visitatori in modo forte e chiaro. E quando non sei disponibile puoi usare le risposte rapide, messaggi preregistrati con cui interagire con i visitatori.\r\nIl tuo video resta al sicuro nel cloud:\r\nCrittografiamo le nostre connessioni. Garantiamo la protezione e la privacy dei dati. E ci impegniamo al massimo perché le tue informazioni siano sempre al sicuro.','111623924503729.jpg',199.99,12,6),(99,'Google Nest Cam Outdoor Bianco, La sicurezza non è mai stata così ottima','Video in diretta 24/7, Tieni sotto controllo la tua casa con un angolo di visione di 130° in HD a 1080p, giorno e notte\r\nAvvisi direttamente sul tuo smartphone, ricevi avvisi di attività in modo da essere sempre al corrente se succede qualcosa quando non sei in casa\r\nResistente agli agenti atmosferici, videocamera, cavo e alimentatore sono tutti resistenti agli agenti atmosferici\r\nParla e ascolta, vedi se c’è qualcuno alla porta e parla per attirare la sua attenzione\r\nVisione chiara come il sole. Anche al buio. La funzione Visione notturna illumina uniformemente tutta la scena. * Per alcune funzionalità, tra cui notifiche per dispositivi mobili, controllo remoto, streaming video e registrazione video, è necessario che Internet e la connessione Wi-Fi funzionino correttamente','121623924531871.jpg',228.99,28,6),(100,'TP-Link Telecamera Wi-Fi Interno, sorveglianza 1080P, Visione Notturna, Notifiche in tempo reale','Hai difficoltà nella configurazione? puoi risolverlo facilmente guardando il video di unboxing e configurazione pubblicato da tp-link in questa pagina\r\nVideo di alta qualità: visione notturna fino a 8 metri, risoluzione 1080p per immagini nitide e chiare; ottieni una visione dettagliata della stanza - movimento orizzontale fino a 360° e movimento verticale fino a 114°\r\nRilevazione movimento e notifiche istantanee: ricevi istantaneamente notifiche push dall\'app quando viene intercettato un movimento; tp-link ha anche fornito una funzione avanzata di impostazione del tempo di registrazione specifico\r\nAllarme acustico e luminoso integrato: innesca un effetto sonoro o luminoso per scoraggiare eventuali visitatori indesiderati\r\nTwo-way audio: audio bidirezionale per comunicare con chi si trova in prossimità della telecamera\r\nSupporta la registrazione in loop: supporto per microsd card fino a 128 gb; e supporta la registrazione in loop: la nuova registrazione sovrascriverà quella vecchia quando la memoria è piena; puoi guardare il video per sapere come abilitare questa funzione\r\nCompatibile con alexa echo show: puoi provare il comando come “alexa, mostra la telecamera della porta principale”; assicurarsi che il firmware sia stato aggiornato all\'ultimo','131623924552333.jpg',29.9,56,6),(101,'AGSHOME Kit Antifurto Con 1 Sirena, 5 Sensori Per Porte E Finestre E 2 Telecomandi','?◆ Potente funzione intelligente ◆ Riproduci un tono di allarme di 120 dB e una notifica APP per i telefoni cellulari per ricordare a te e alla tua famiglia che qualcuno è partito o è entrato in casa. Il WiFi è collegato all\'app Smart Life, che può essere controllata a distanza sia che tu sia a casa o in viaggio. Devi proteggere la tua casa o il tuo ufficio.\r\n?◆ MIGLIORE SOLUZIONE DEL KIT ◆ Il kit del sistema di allarme domestico W2 contiene 1 * stazione base, 5 * sensori porta, 2 * telecomando, 1 * alimentatore, 1 * manuale utente, nonché la staffa di montaggio e il nastro adesivo. Tutti i sensori e i telecomandi sono già collegati alla stazione base per risparmiare tempo. NESSUN costo di installazione o canone mensile di monitoraggio.\r\n?◆ mini stazione base ◆ stazione base è un allarme, molto piccolo e facile da usare. La connessione wireless non danneggia la parete. Si consiglia di utilizzare sempre un alimentatore. La batteria funziona per diverse ore, solo come batteria di emergenza.\r\n?◆ 2 telecomandi ◆ Con il telecomando, le opzioni di disinserimento, squillo, allarme e panico possono essere impostate entro 15 metri. Con il nastro antigravità (incluso), è possibile collegare il telecomando di allarme per porte aperte in qualsiasi posizione e rimuoverlo in qualsiasi momento.','141623924607473.jpg',79.99,3,6),(102,'Sistema di Allarme Intelligente con Integrazione Alexa/Google, ERAY H3 Sistema di Sicurezza Wireless','[Smart & No Charges]: sistema di allarme ERAY H3 WLAN (2.4 G WiFi), tutte le impostazioni possono essere effettuate tramite app (iOS / Android) ed è compatibile con Alexa e Google. Puoi ricevere App Push in caso di allarme, non devi pagare alcun canone mensile\r\n[Multifunzione]: attivazione ritardata (solo tramite telecomando) e allarme, attivazione e disattivazione temporizzata, diverse zone tra cui scegliere, nome zona modificabile, creazione scena, durata allarme, suono allarme, ecc.\r\n[Espandibile]: questo sistema di allarme intelligente supporta max. 8 telecomandi, 50 sensori e molte sirene wireless. È possibile aggiungere ulteriori sensori o telecamera IP e presa in base alle proprie esigenze\r\n[Nota]: Si consiglia di collegare sempre la sirena all\'alimentazione, la batteria (batteria di emergenza) funziona solo per poche ore. Non installare il sensore della porta vicino al metallo. Anche la temperatura, la luce solare e gli oggetti in movimento possono causare falsi allarmi dal sensore di movimento\r\n[Garanzia ERAY e volume di consegna]: 12 mesi di garanzia, 30 giorni di diritto di restituzione, servizio clienti cordiale. 1x centrale di allarme (sirena), 1x rilevatore di movimento, 2x sensore porta, 1x pulsante campanello, 2x telecomando, 1x istruzioni per l\'uso in italiano e inglese','151623924630421.jpg',89.99,14,6),(103,'Google Nest Protect, White, Il rilevatore di Fumo che fa Invidia a Tutti','Il rilevatore di fumo che fa invidia a tutti, Nest Protect è stato progettato per aiutarti a stare al sicuro a casa tua, dispone di un sensore di fumo a spettro suddiviso, può essere silenziato direttamente dallo smartphone, esegue operazioni di diagnostica automatica e assicura un ciclo di vita di 10 anni, ti dice cosa c\'è che non va e può persino inviarti avvisi sullo smartphone\r\nSei sempre al corrente di quello che succede, ovunque ti trovi, Nest Protect ti invia avvisi sullo smartphone se rileva che qualcosa non va, riceverai un messaggio se l\'allarme si attiva, le batterie stanno per esaurirsi o un sensore non funziona correttamente\r\nRileva incendi a combustione rapida e lenta, il sensore a spettro suddiviso utilizza due lunghezze d\'onda della luce per rilevare la presenza di fumo\r\nLa sicurezza non deve essere fastidiosa, il problema erano solo i popcorn bruciacchiati, ricevi un avviso che puoi silenziare direttamente dallo smartphone\r\nIl dispositivo ti dice se c\'è fumo o monossido di carbonio quando qualcosa non va, tutti i tuoi Nest Protect parlano in contemporanea per dirti cosa non va e dove risiede il pericolo','161623924654218.jpg',129.99,18,6),(104,'Zigbee Sensore SONOFF SNZB-04 per porte senza fili, antifurto per la sicurezza domestica','✔ Il sensore per porte / finestre wireless SONOFF può essere messo sulla tua porta o finestra per avvisarti con una notifica di avviso se la tua porta o finestra è aperta o chiusa, aggiungendo un ulteriore livello di sicurezza alla tua casa. Sia lo stato di apertura che di chiusura della porta / finestra sono sincronizzati sul telefono cellulare, per non pensare più se la porta è spenta o meno.\r\n✔ È necessario SONOFF ZigBee Bridge. Il sensore offre la possibilità di creare un collegamento scena di dispositivi ZigBee e Wi-Fi. Questo sensore è in grado di aiutarti a creare una casa intelligente. Funziona in armonia con SONOFF ZigBee Bridge, puoi creare una scena per illuminare immediatamente il corridoio quando la porta viene aperta.\r\n✔ Installazione semplice per una configurazione più semplice, l\'installazione dell\'adesivo 3M (inclusa) consente di risparmiare notevolmente tempo. La sua applicazione può anche estendersi ad ampie aree come cassetti, porte da garage, tende, ecc. Basta attaccarlo dove due cose si collegano ed è sempre sicuro, anche se non ci prestiamo attenzione.\r\n✔ La funzione della fotocamera può essere abilitata sulla pagina del sensore nell\'APP eWeLink, che consente anche di vedere chiaramente chi entra nella tua casa; anche tu puoi spaventare lo sconosciuto e chiedergli di uscire di casa immediatamente con la funzione audio a due vie.','171623924749863.jpg',16.99,78,6),(105,'Allarme WiFi con sensore per porte e della finestre, Sistema di sicurezza senza fili controllo APP','【 Connessione Wifi】 Collegando la connessione WIFI (solo 2.4G) e la connessione del telefono cellulare, sia che siate in casa o in viaggio, il sensore di contatto WiFi intelligente porterà la notifica per avvisarvi quando la porta/o la finestra è aperta o chiusa. (Nota: supporta solo Wi-Fi 2.4 GHz).\r\n【Semplice da usare】Semplice installazione e apre il sensore porta in pochi minuti, senza bisogno di strumenti o di un\'installazione professionale. Aggiungere altri componenti in qualsiasi momento per la protezione dell\'intera casa. Non richiede alcun hub o servizio di abbonamento. Basta collegarlo alla rete Wi-Fi e scaricare la Smartlife App dall\'App Store. Nessun pagamento mensile\r\n【Compatibile con Alexa e Google Home】 Abbina la nostra applicazione Smartlife con Alexa o Google Home e divertiti a conoscere lo stato del vostro sensore della porta senza bisogno di usare il telefono. Alexa / Google è in grado di rispondere alla domanda \"La porta è aperta/o chiusa?\r\n【Lunga durata della batteria 】: Il sensore della porta a basso consumo energetico garantisce una durata della batteria di circa 1 anno. Super-lunga durata della batteria evita la sostituzione frequentemente, offrendo una migliore esperienza. Inoltre, è possibile sostituire la batteria CR2 quando la potenza del sensore della porta aperta è bassa.','181623924778024.jpg',24.99,13,6); +/*!40000 ALTER TABLE `prodotto` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `utente` +-- + +DROP TABLE IF EXISTS `utente`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `utente` ( + `userId` int NOT NULL AUTO_INCREMENT, + `nome` varchar(50) DEFAULT NULL, + `email` varchar(45) NOT NULL, + `passwordHash` varchar(128) NOT NULL, + `salt` varchar(32) NOT NULL, + `indirizzo` varchar(100) DEFAULT NULL, + `telefono` varchar(20) DEFAULT NULL, + `admin` tinyint DEFAULT '0', + PRIMARY KEY (`userId`), + UNIQUE KEY `email_UNIQUE` (`email`), + UNIQUE KEY `salt_UNIQUE` (`salt`) +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `utente` +-- + +LOCK TABLES `utente` WRITE; +/*!40000 ALTER TABLE `utente` DISABLE KEYS */; +INSERT INTO `utente` VALUES (1,'Giovanni Donisi','g.donisi1@studenti.unisa.it','15f1fea55204a6b5522ff589ca993eec45a358e17918d08d4d2dd43a2198cb85596c32a851871647fb0c612916b0bc2998bbaa19b02bdb260b6fa8da64b0e21b','57c8ddfe8d4643b33346ed0eb61c35a5','Via Napoli 32','3771112233',1),(4,'E','uitgetrapt@sumakay.com','66b5136b86d42eb1f98d850e754821866b12f0f4e179bda94b8f54294d754f41d4028f136de09bdbc5ff376dc8ce2e62fd4297237cf2df6161577229ef9afe7a','0b53eff3e7887d3b1290f57dc9482256',NULL,NULL,0),(5,'Gio Donisi','giovannidonisi0701@gmail.com','36dfc74852c9cb64ef339be921c84dd057315368b9563cda46d1fe2584264c2f057159b13ffae4e9edf9a86704ae28cd9ee08a4cb66dc9928f7421c11c38878e','2bb33026f4bdf33896d974124b21eed5','Via Roma','3333333333',0),(6,'Mario Rossi','gisi1@sta.it','477cde94469988239f7c8ca9979f2aa2f9a52915b7e74d68344967a8261ad8c88e9beb47fc3a76dcd725eaa7e0507edd05a175bfa43c2b4265798c0f84302b0c','aed5d5b270e88d9e2044893cbc5cad51',NULL,NULL,0),(7,'Antonio Prova','jevndgnet@mobinum.com','d1cb69f45a65a77ab438a9497bfecc6cfa5b38d9db788a4455d3313a2791b2a41e15db8625cc8d267b37f5132de671881152edfd30fb33b62483d2131ed1956a','7428757aa37819c28975f1b4b0e763e1',NULL,NULL,0),(9,'Mario Palumbo','nambikua@queersinproperty.com','7caf6b8f6035aa3199d42b69301d6ec150cc61e88b20518a991eeab2efa63803016f7887539b4bd178782b59a82f074124e51e1abe81512b00b078b921ab3bb4','94bd72d02a031c54c1b2be1794d5f656','Piazza Risorgimento 1','3433433433',0),(10,'Ezio A','plasto@bookst.site','1fce8005d98a712b885cecbb4c3a9692d453493b6226d30d8ffe6c048c645168e502434ee8f30e7a42157358ba823fda83101495c72d74126af044985fcc16bc','02ad27797670a2116e0d6e3d94219484','Via Via 22','33333333338',0); +/*!40000 ALTER TABLE `utente` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2021-06-18 14:15:49 diff --git a/404.html b/gh-pages/404.html similarity index 100% rename from 404.html rename to gh-pages/404.html diff --git a/carrello.html b/gh-pages/carrello.html similarity index 100% rename from carrello.html rename to gh-pages/carrello.html diff --git a/categoria.html b/gh-pages/categoria.html similarity index 100% rename from categoria.html rename to gh-pages/categoria.html diff --git a/checkout.html b/gh-pages/checkout.html similarity index 100% rename from checkout.html rename to gh-pages/checkout.html diff --git a/css/checkout.css b/gh-pages/css/checkout.css similarity index 100% rename from css/checkout.css rename to gh-pages/css/checkout.css diff --git a/css/errorPage.css b/gh-pages/css/errorPage.css similarity index 100% rename from css/errorPage.css rename to gh-pages/css/errorPage.css diff --git a/css/footer.css b/gh-pages/css/footer.css similarity index 100% rename from css/footer.css rename to gh-pages/css/footer.css diff --git a/css/form.css b/gh-pages/css/form.css similarity index 100% rename from css/form.css rename to gh-pages/css/form.css diff --git a/css/header.css b/gh-pages/css/header.css similarity index 100% rename from css/header.css rename to gh-pages/css/header.css diff --git a/css/home.css b/gh-pages/css/home.css similarity index 100% rename from css/home.css rename to gh-pages/css/home.css diff --git a/css/normalize.css b/gh-pages/css/normalize.css similarity index 100% rename from css/normalize.css rename to gh-pages/css/normalize.css diff --git a/css/prodotto.css b/gh-pages/css/prodotto.css similarity index 100% rename from css/prodotto.css rename to gh-pages/css/prodotto.css diff --git a/css/style.css b/gh-pages/css/style.css similarity index 100% rename from css/style.css rename to gh-pages/css/style.css diff --git a/css/table.css b/gh-pages/css/table.css similarity index 100% rename from css/table.css rename to gh-pages/css/table.css diff --git a/gh-pages/fonts/Poppins-Italic.ttf b/gh-pages/fonts/Poppins-Italic.ttf new file mode 100644 index 0000000..4620399 Binary files /dev/null and b/gh-pages/fonts/Poppins-Italic.ttf differ diff --git a/gh-pages/fonts/Poppins-Regular.ttf b/gh-pages/fonts/Poppins-Regular.ttf new file mode 100644 index 0000000..be06e7f Binary files /dev/null and b/gh-pages/fonts/Poppins-Regular.ttf differ diff --git a/gh-pages/fonts/Poppins-SemiBold.ttf b/gh-pages/fonts/Poppins-SemiBold.ttf new file mode 100644 index 0000000..dabf7c2 Binary files /dev/null and b/gh-pages/fonts/Poppins-SemiBold.ttf differ diff --git a/gh-pages/fonts/Poppins-SemiBoldItalic.ttf b/gh-pages/fonts/Poppins-SemiBoldItalic.ttf new file mode 100644 index 0000000..29d5f74 Binary files /dev/null and b/gh-pages/fonts/Poppins-SemiBoldItalic.ttf differ diff --git a/gh-pages/icons/account.svg b/gh-pages/icons/account.svg new file mode 100644 index 0000000..a57eb30 --- /dev/null +++ b/gh-pages/icons/account.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/bulb.svg b/gh-pages/icons/bulb.svg new file mode 100644 index 0000000..25dfa80 --- /dev/null +++ b/gh-pages/icons/bulb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/cart.svg b/gh-pages/icons/cart.svg new file mode 100644 index 0000000..d873445 --- /dev/null +++ b/gh-pages/icons/cart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/close.svg b/gh-pages/icons/close.svg new file mode 100644 index 0000000..f8914bb --- /dev/null +++ b/gh-pages/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/email.svg b/gh-pages/icons/email.svg new file mode 100644 index 0000000..032c011 --- /dev/null +++ b/gh-pages/icons/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/facebook.svg b/gh-pages/icons/facebook.svg new file mode 100644 index 0000000..077096f --- /dev/null +++ b/gh-pages/icons/facebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/favicon.ico b/gh-pages/icons/favicon.ico new file mode 100644 index 0000000..66e0db3 Binary files /dev/null and b/gh-pages/icons/favicon.ico differ diff --git a/gh-pages/icons/home.svg b/gh-pages/icons/home.svg new file mode 100644 index 0000000..8d95673 --- /dev/null +++ b/gh-pages/icons/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/instagram.svg b/gh-pages/icons/instagram.svg new file mode 100644 index 0000000..c7d60eb --- /dev/null +++ b/gh-pages/icons/instagram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/login.svg b/gh-pages/icons/login.svg new file mode 100644 index 0000000..f864993 --- /dev/null +++ b/gh-pages/icons/login.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/logo.png b/gh-pages/icons/logo.png new file mode 100644 index 0000000..e7009d2 Binary files /dev/null and b/gh-pages/icons/logo.png differ diff --git a/gh-pages/icons/logo512.png b/gh-pages/icons/logo512.png new file mode 100644 index 0000000..42d8366 Binary files /dev/null and b/gh-pages/icons/logo512.png differ diff --git a/gh-pages/icons/logout.svg b/gh-pages/icons/logout.svg new file mode 100644 index 0000000..fa99510 --- /dev/null +++ b/gh-pages/icons/logout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/menu.svg b/gh-pages/icons/menu.svg new file mode 100644 index 0000000..a6136ae --- /dev/null +++ b/gh-pages/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/search.svg b/gh-pages/icons/search.svg new file mode 100644 index 0000000..4d545cc --- /dev/null +++ b/gh-pages/icons/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/top.svg b/gh-pages/icons/top.svg new file mode 100644 index 0000000..3dc74c3 --- /dev/null +++ b/gh-pages/icons/top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/twitter.svg b/gh-pages/icons/twitter.svg new file mode 100644 index 0000000..0a2f9c3 --- /dev/null +++ b/gh-pages/icons/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gh-pages/icons/ylogo.png b/gh-pages/icons/ylogo.png new file mode 100644 index 0000000..2afc38c Binary files /dev/null and b/gh-pages/icons/ylogo.png differ diff --git a/gh-pages/icons/youtube.svg b/gh-pages/icons/youtube.svg new file mode 100644 index 0000000..e8d4f34 --- /dev/null +++ b/gh-pages/icons/youtube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/001623855415265.webp b/gh-pages/images/001623855415265.webp similarity index 100% rename from images/001623855415265.webp rename to gh-pages/images/001623855415265.webp diff --git a/images/021622885267162.webp b/gh-pages/images/021622885267162.webp similarity index 100% rename from images/021622885267162.webp rename to gh-pages/images/021622885267162.webp diff --git a/images/031623854785787.webp b/gh-pages/images/031623854785787.webp similarity index 100% rename from images/031623854785787.webp rename to gh-pages/images/031623854785787.webp diff --git a/images/051623240913086.webp b/gh-pages/images/051623240913086.webp similarity index 100% rename from images/051623240913086.webp rename to gh-pages/images/051623240913086.webp diff --git a/images/081623241064448.webp b/gh-pages/images/081623241064448.webp similarity index 100% rename from images/081623241064448.webp rename to gh-pages/images/081623241064448.webp diff --git a/images/081623855213031.webp b/gh-pages/images/081623855213031.webp similarity index 100% rename from images/081623855213031.webp rename to gh-pages/images/081623855213031.webp diff --git a/images/101623241136837.webp b/gh-pages/images/101623241136837.webp similarity index 100% rename from images/101623241136837.webp rename to gh-pages/images/101623241136837.webp diff --git a/images/121623509931202.webp b/gh-pages/images/121623509931202.webp similarity index 100% rename from images/121623509931202.webp rename to gh-pages/images/121623509931202.webp diff --git a/images/121623924531871.webp b/gh-pages/images/121623924531871.webp similarity index 100% rename from images/121623924531871.webp rename to gh-pages/images/121623924531871.webp diff --git a/images/171622891033384.webp b/gh-pages/images/171622891033384.webp similarity index 100% rename from images/171622891033384.webp rename to gh-pages/images/171622891033384.webp diff --git a/images/211622891129354.webp b/gh-pages/images/211622891129354.webp similarity index 100% rename from images/211622891129354.webp rename to gh-pages/images/211622891129354.webp diff --git a/images/301622885258929.webp b/gh-pages/images/301622885258929.webp similarity index 100% rename from images/301622885258929.webp rename to gh-pages/images/301622885258929.webp diff --git a/images/371622885080660.webp b/gh-pages/images/371622885080660.webp similarity index 100% rename from images/371622885080660.webp rename to gh-pages/images/371622885080660.webp diff --git a/images/721622885064172.webp b/gh-pages/images/721622885064172.webp similarity index 100% rename from images/721622885064172.webp rename to gh-pages/images/721622885064172.webp diff --git a/images/841622885280097.webp b/gh-pages/images/841622885280097.webp similarity index 100% rename from images/841622885280097.webp rename to gh-pages/images/841622885280097.webp diff --git a/images/911622885248542.webp b/gh-pages/images/911622885248542.webp similarity index 100% rename from images/911622885248542.webp rename to gh-pages/images/911622885248542.webp diff --git a/index.html b/gh-pages/index.html similarity index 100% rename from index.html rename to gh-pages/index.html diff --git a/js/jquery-3.6.0.min.js b/gh-pages/js/jquery-3.6.0.min.js similarity index 100% rename from js/jquery-3.6.0.min.js rename to gh-pages/js/jquery-3.6.0.min.js diff --git a/login.html b/gh-pages/login.html similarity index 100% rename from login.html rename to gh-pages/login.html diff --git a/privacyPolicy.html b/gh-pages/privacyPolicy.html similarity index 100% rename from privacyPolicy.html rename to gh-pages/privacyPolicy.html diff --git a/prodotti.html b/gh-pages/prodotti.html similarity index 100% rename from prodotti.html rename to gh-pages/prodotti.html diff --git a/prodotto.html b/gh-pages/prodotto.html similarity index 100% rename from prodotto.html rename to gh-pages/prodotto.html diff --git a/search.html b/gh-pages/search.html similarity index 100% rename from search.html rename to gh-pages/search.html diff --git a/signup.html b/gh-pages/signup.html similarity index 100% rename from signup.html rename to gh-pages/signup.html diff --git a/server.xml b/server.xml new file mode 100644 index 0000000..96b08ed --- /dev/null +++ b/server.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uploads/001623237621342.jpg b/uploads/001623237621342.jpg new file mode 100644 index 0000000..f2e3a4c Binary files /dev/null and b/uploads/001623237621342.jpg differ diff --git a/uploads/001623509602619.jpg b/uploads/001623509602619.jpg new file mode 100644 index 0000000..9599a38 Binary files /dev/null and b/uploads/001623509602619.jpg differ diff --git a/uploads/001623854705781.jpg b/uploads/001623854705781.jpg new file mode 100644 index 0000000..e368e54 Binary files /dev/null and b/uploads/001623854705781.jpg differ diff --git a/uploads/001623855415265.jpg b/uploads/001623855415265.jpg new file mode 100644 index 0000000..fbd061e Binary files /dev/null and b/uploads/001623855415265.jpg differ diff --git a/uploads/001623924176953.jpg b/uploads/001623924176953.jpg new file mode 100644 index 0000000..cf8e1ad Binary files /dev/null and b/uploads/001623924176953.jpg differ diff --git a/uploads/011622814497642.jpg b/uploads/011622814497642.jpg new file mode 100644 index 0000000..fd9ce97 Binary files /dev/null and b/uploads/011622814497642.jpg differ diff --git a/uploads/011623238555528.jpg b/uploads/011623238555528.jpg new file mode 100644 index 0000000..42e673a Binary files /dev/null and b/uploads/011623238555528.jpg differ diff --git a/uploads/011623509630233.jpg b/uploads/011623509630233.jpg new file mode 100644 index 0000000..6979bfa Binary files /dev/null and b/uploads/011623509630233.jpg differ diff --git a/uploads/011623854726428.jpg b/uploads/011623854726428.jpg new file mode 100644 index 0000000..1a05011 Binary files /dev/null and b/uploads/011623854726428.jpg differ diff --git a/uploads/011623855449464.jpg b/uploads/011623855449464.jpg new file mode 100644 index 0000000..9889e90 Binary files /dev/null and b/uploads/011623855449464.jpg differ diff --git a/uploads/011623924202903.jpg b/uploads/011623924202903.jpg new file mode 100644 index 0000000..2454191 Binary files /dev/null and b/uploads/011623924202903.jpg differ diff --git a/uploads/021622814747858.jpg b/uploads/021622814747858.jpg new file mode 100644 index 0000000..9a1eb96 Binary files /dev/null and b/uploads/021622814747858.jpg differ diff --git a/uploads/021622885267162.jpg b/uploads/021622885267162.jpg new file mode 100644 index 0000000..8412388 Binary files /dev/null and b/uploads/021622885267162.jpg differ diff --git a/uploads/021623238688922.jpg b/uploads/021623238688922.jpg new file mode 100644 index 0000000..c716d7f Binary files /dev/null and b/uploads/021623238688922.jpg differ diff --git a/uploads/021623509653952.jpg b/uploads/021623509653952.jpg new file mode 100644 index 0000000..7487d01 Binary files /dev/null and b/uploads/021623509653952.jpg differ diff --git a/uploads/021623854757903.jpg b/uploads/021623854757903.jpg new file mode 100644 index 0000000..4cc4f03 Binary files /dev/null and b/uploads/021623854757903.jpg differ diff --git a/uploads/021623855478511.jpg b/uploads/021623855478511.jpg new file mode 100644 index 0000000..136a185 Binary files /dev/null and b/uploads/021623855478511.jpg differ diff --git a/uploads/021623924222215.jpg b/uploads/021623924222215.jpg new file mode 100644 index 0000000..bfc6ffb Binary files /dev/null and b/uploads/021623924222215.jpg differ diff --git a/uploads/031622814902059.jpg b/uploads/031622814902059.jpg new file mode 100644 index 0000000..4b48926 Binary files /dev/null and b/uploads/031622814902059.jpg differ diff --git a/uploads/031623237706930.jpg b/uploads/031623237706930.jpg new file mode 100644 index 0000000..aea22ad Binary files /dev/null and b/uploads/031623237706930.jpg differ diff --git a/uploads/031623509688015.jpg b/uploads/031623509688015.jpg new file mode 100644 index 0000000..92e8e4c Binary files /dev/null and b/uploads/031623509688015.jpg differ diff --git a/uploads/031623854785787.jpg b/uploads/031623854785787.jpg new file mode 100644 index 0000000..6c8b329 Binary files /dev/null and b/uploads/031623854785787.jpg differ diff --git a/uploads/031623855504724.jpg b/uploads/031623855504724.jpg new file mode 100644 index 0000000..fb5b669 Binary files /dev/null and b/uploads/031623855504724.jpg differ diff --git a/uploads/031623924247306.jpg b/uploads/031623924247306.jpg new file mode 100644 index 0000000..1c20ded Binary files /dev/null and b/uploads/031623924247306.jpg differ diff --git a/uploads/041622814930331.jpg b/uploads/041622814930331.jpg new file mode 100644 index 0000000..2a4eb02 Binary files /dev/null and b/uploads/041622814930331.jpg differ diff --git a/uploads/041623240878026.jpg b/uploads/041623240878026.jpg new file mode 100644 index 0000000..38277ca Binary files /dev/null and b/uploads/041623240878026.jpg differ diff --git a/uploads/041623509717333.jpg b/uploads/041623509717333.jpg new file mode 100644 index 0000000..1b2e480 Binary files /dev/null and b/uploads/041623509717333.jpg differ diff --git a/uploads/041623854818975.jpg b/uploads/041623854818975.jpg new file mode 100644 index 0000000..d1eeaf8 Binary files /dev/null and b/uploads/041623854818975.jpg differ diff --git a/uploads/041623855539037.jpg b/uploads/041623855539037.jpg new file mode 100644 index 0000000..1291c94 Binary files /dev/null and b/uploads/041623855539037.jpg differ diff --git a/uploads/041623924266842.jpg b/uploads/041623924266842.jpg new file mode 100644 index 0000000..bbb1602 Binary files /dev/null and b/uploads/041623924266842.jpg differ diff --git a/uploads/051622814986584.jpg b/uploads/051622814986584.jpg new file mode 100644 index 0000000..57f4703 Binary files /dev/null and b/uploads/051622814986584.jpg differ diff --git a/uploads/051623240913086.jpg b/uploads/051623240913086.jpg new file mode 100644 index 0000000..607b0ac Binary files /dev/null and b/uploads/051623240913086.jpg differ diff --git a/uploads/051623509743380.jpg b/uploads/051623509743380.jpg new file mode 100644 index 0000000..1191e6a Binary files /dev/null and b/uploads/051623509743380.jpg differ diff --git a/uploads/051623854847416.jpg b/uploads/051623854847416.jpg new file mode 100644 index 0000000..6ff2fdc Binary files /dev/null and b/uploads/051623854847416.jpg differ diff --git a/uploads/051623855562761.jpg b/uploads/051623855562761.jpg new file mode 100644 index 0000000..ff7f109 Binary files /dev/null and b/uploads/051623855562761.jpg differ diff --git a/uploads/051623924295765.jpg b/uploads/051623924295765.jpg new file mode 100644 index 0000000..990e014 Binary files /dev/null and b/uploads/051623924295765.jpg differ diff --git a/uploads/061622815040665.jpg b/uploads/061622815040665.jpg new file mode 100644 index 0000000..2865568 Binary files /dev/null and b/uploads/061622815040665.jpg differ diff --git a/uploads/061623240987420.jpg b/uploads/061623240987420.jpg new file mode 100644 index 0000000..751f751 Binary files /dev/null and b/uploads/061623240987420.jpg differ diff --git a/uploads/061623509773219.jpg b/uploads/061623509773219.jpg new file mode 100644 index 0000000..a91cb8b Binary files /dev/null and b/uploads/061623509773219.jpg differ diff --git a/uploads/061623854868412.jpg b/uploads/061623854868412.jpg new file mode 100644 index 0000000..2909036 Binary files /dev/null and b/uploads/061623854868412.jpg differ diff --git a/uploads/061623855586328.jpg b/uploads/061623855586328.jpg new file mode 100644 index 0000000..e0f3858 Binary files /dev/null and b/uploads/061623855586328.jpg differ diff --git a/uploads/061623924316806.jpg b/uploads/061623924316806.jpg new file mode 100644 index 0000000..5fa8ebc Binary files /dev/null and b/uploads/061623924316806.jpg differ diff --git a/uploads/071622815079162.jpg b/uploads/071622815079162.jpg new file mode 100644 index 0000000..d8860fb Binary files /dev/null and b/uploads/071622815079162.jpg differ diff --git a/uploads/071623241021792.jpg b/uploads/071623241021792.jpg new file mode 100644 index 0000000..aa36899 Binary files /dev/null and b/uploads/071623241021792.jpg differ diff --git a/uploads/071623509797777.jpg b/uploads/071623509797777.jpg new file mode 100644 index 0000000..b27545d Binary files /dev/null and b/uploads/071623509797777.jpg differ diff --git a/uploads/071623854896533.jpg b/uploads/071623854896533.jpg new file mode 100644 index 0000000..8436438 Binary files /dev/null and b/uploads/071623854896533.jpg differ diff --git a/uploads/071623855613417.jpg b/uploads/071623855613417.jpg new file mode 100644 index 0000000..f25306b Binary files /dev/null and b/uploads/071623855613417.jpg differ diff --git a/uploads/071623924340111.jpg b/uploads/071623924340111.jpg new file mode 100644 index 0000000..f60d7b6 Binary files /dev/null and b/uploads/071623924340111.jpg differ diff --git a/uploads/081622815119691.jpg b/uploads/081622815119691.jpg new file mode 100644 index 0000000..df4d64c Binary files /dev/null and b/uploads/081622815119691.jpg differ diff --git a/uploads/081623241064448.jpg b/uploads/081623241064448.jpg new file mode 100644 index 0000000..17e8da7 Binary files /dev/null and b/uploads/081623241064448.jpg differ diff --git a/uploads/081623509824064.jpg b/uploads/081623509824064.jpg new file mode 100644 index 0000000..b34229c Binary files /dev/null and b/uploads/081623509824064.jpg differ diff --git a/uploads/081623855213031.jpg b/uploads/081623855213031.jpg new file mode 100644 index 0000000..c3d3c1c Binary files /dev/null and b/uploads/081623855213031.jpg differ diff --git a/uploads/081623855633667.jpg b/uploads/081623855633667.jpg new file mode 100644 index 0000000..bdb988c Binary files /dev/null and b/uploads/081623855633667.jpg differ diff --git a/uploads/081623924382060.jpg b/uploads/081623924382060.jpg new file mode 100644 index 0000000..364c64d Binary files /dev/null and b/uploads/081623924382060.jpg differ diff --git a/uploads/091622888763359.jpg b/uploads/091622888763359.jpg new file mode 100644 index 0000000..4ab8d66 Binary files /dev/null and b/uploads/091622888763359.jpg differ diff --git a/uploads/091623241091101.jpg b/uploads/091623241091101.jpg new file mode 100644 index 0000000..62cb1bf Binary files /dev/null and b/uploads/091623241091101.jpg differ diff --git a/uploads/091623509853124.jpg b/uploads/091623509853124.jpg new file mode 100644 index 0000000..9f79e30 Binary files /dev/null and b/uploads/091623509853124.jpg differ diff --git a/uploads/091623855241919.jpg b/uploads/091623855241919.jpg new file mode 100644 index 0000000..fa08808 Binary files /dev/null and b/uploads/091623855241919.jpg differ diff --git a/uploads/091623855666042.jpg b/uploads/091623855666042.jpg new file mode 100644 index 0000000..a56a0d1 Binary files /dev/null and b/uploads/091623855666042.jpg differ diff --git a/uploads/091623924421594.jpg b/uploads/091623924421594.jpg new file mode 100644 index 0000000..c4afe9b Binary files /dev/null and b/uploads/091623924421594.jpg differ diff --git a/uploads/101622888801242.jpg b/uploads/101622888801242.jpg new file mode 100644 index 0000000..215c6c9 Binary files /dev/null and b/uploads/101622888801242.jpg differ diff --git a/uploads/101623241136837.jpg b/uploads/101623241136837.jpg new file mode 100644 index 0000000..f0edc0c Binary files /dev/null and b/uploads/101623241136837.jpg differ diff --git a/uploads/101623509883059.jpg b/uploads/101623509883059.jpg new file mode 100644 index 0000000..68ef835 Binary files /dev/null and b/uploads/101623509883059.jpg differ diff --git a/uploads/101623855270948.jpg b/uploads/101623855270948.jpg new file mode 100644 index 0000000..ddcb852 Binary files /dev/null and b/uploads/101623855270948.jpg differ diff --git a/uploads/101623855686882.jpg b/uploads/101623855686882.jpg new file mode 100644 index 0000000..6a617d3 Binary files /dev/null and b/uploads/101623855686882.jpg differ diff --git a/uploads/101623924457687.jpg b/uploads/101623924457687.jpg new file mode 100644 index 0000000..a6bac12 Binary files /dev/null and b/uploads/101623924457687.jpg differ diff --git a/uploads/111622890624827.jpg b/uploads/111622890624827.jpg new file mode 100644 index 0000000..24b5fc9 Binary files /dev/null and b/uploads/111622890624827.jpg differ diff --git a/uploads/111623241166344.jpg b/uploads/111623241166344.jpg new file mode 100644 index 0000000..9dfbf4a Binary files /dev/null and b/uploads/111623241166344.jpg differ diff --git a/uploads/111623509908502.jpg b/uploads/111623509908502.jpg new file mode 100644 index 0000000..453c07e Binary files /dev/null and b/uploads/111623509908502.jpg differ diff --git a/uploads/111623855300947.jpg b/uploads/111623855300947.jpg new file mode 100644 index 0000000..d6ad440 Binary files /dev/null and b/uploads/111623855300947.jpg differ diff --git a/uploads/111623855704451.jpg b/uploads/111623855704451.jpg new file mode 100644 index 0000000..58bd91d Binary files /dev/null and b/uploads/111623855704451.jpg differ diff --git a/uploads/111623924503729.jpg b/uploads/111623924503729.jpg new file mode 100644 index 0000000..68ae959 Binary files /dev/null and b/uploads/111623924503729.jpg differ diff --git a/uploads/121622890666959.jpg b/uploads/121622890666959.jpg new file mode 100644 index 0000000..8e3ba24 Binary files /dev/null and b/uploads/121622890666959.jpg differ diff --git a/uploads/121623509931202.jpg b/uploads/121623509931202.jpg new file mode 100644 index 0000000..a55ab9e Binary files /dev/null and b/uploads/121623509931202.jpg differ diff --git a/uploads/121623855323230.jpg b/uploads/121623855323230.jpg new file mode 100644 index 0000000..3febf0f Binary files /dev/null and b/uploads/121623855323230.jpg differ diff --git a/uploads/121623855719875.jpg b/uploads/121623855719875.jpg new file mode 100644 index 0000000..6203618 Binary files /dev/null and b/uploads/121623855719875.jpg differ diff --git a/uploads/121623924531871.jpg b/uploads/121623924531871.jpg new file mode 100644 index 0000000..053428d Binary files /dev/null and b/uploads/121623924531871.jpg differ diff --git a/uploads/131622890697268.jpg b/uploads/131622890697268.jpg new file mode 100644 index 0000000..874bc3d Binary files /dev/null and b/uploads/131622890697268.jpg differ diff --git a/uploads/131623924552333.jpg b/uploads/131623924552333.jpg new file mode 100644 index 0000000..5a0a871 Binary files /dev/null and b/uploads/131623924552333.jpg differ diff --git a/uploads/141622890726398.jpg b/uploads/141622890726398.jpg new file mode 100644 index 0000000..0384ff3 Binary files /dev/null and b/uploads/141622890726398.jpg differ diff --git a/uploads/141623924607473.jpg b/uploads/141623924607473.jpg new file mode 100644 index 0000000..9a28037 Binary files /dev/null and b/uploads/141623924607473.jpg differ diff --git a/uploads/151622890959721.jpg b/uploads/151622890959721.jpg new file mode 100644 index 0000000..3d97de2 Binary files /dev/null and b/uploads/151622890959721.jpg differ diff --git a/uploads/151623924630421.jpg b/uploads/151623924630421.jpg new file mode 100644 index 0000000..e8a88e8 Binary files /dev/null and b/uploads/151623924630421.jpg differ diff --git a/uploads/161622891005646.jpg b/uploads/161622891005646.jpg new file mode 100644 index 0000000..d56ada8 Binary files /dev/null and b/uploads/161622891005646.jpg differ diff --git a/uploads/161623924654218.jpg b/uploads/161623924654218.jpg new file mode 100644 index 0000000..43bb149 Binary files /dev/null and b/uploads/161623924654218.jpg differ diff --git a/uploads/171622891033384.jpg b/uploads/171622891033384.jpg new file mode 100644 index 0000000..ac7d32e Binary files /dev/null and b/uploads/171622891033384.jpg differ diff --git a/uploads/171623924749863.jpg b/uploads/171623924749863.jpg new file mode 100644 index 0000000..de5a886 Binary files /dev/null and b/uploads/171623924749863.jpg differ diff --git a/uploads/181622891072073.jpg b/uploads/181622891072073.jpg new file mode 100644 index 0000000..b7e1691 Binary files /dev/null and b/uploads/181622891072073.jpg differ diff --git a/uploads/181623924778024.jpg b/uploads/181623924778024.jpg new file mode 100644 index 0000000..3411c9c Binary files /dev/null and b/uploads/181623924778024.jpg differ diff --git a/uploads/191622891095495.jpg b/uploads/191622891095495.jpg new file mode 100644 index 0000000..1f3b646 Binary files /dev/null and b/uploads/191622891095495.jpg differ diff --git a/uploads/201622891151461.jpg b/uploads/201622891151461.jpg new file mode 100644 index 0000000..fbe9f48 Binary files /dev/null and b/uploads/201622891151461.jpg differ diff --git a/uploads/211622891129354.jpg b/uploads/211622891129354.jpg new file mode 100644 index 0000000..c34c1b1 Binary files /dev/null and b/uploads/211622891129354.jpg differ diff --git a/uploads/221622814263441.jpg b/uploads/221622814263441.jpg new file mode 100644 index 0000000..c6142fa Binary files /dev/null and b/uploads/221622814263441.jpg differ diff --git a/uploads/301622885258929.jpg b/uploads/301622885258929.jpg new file mode 100644 index 0000000..ff5c786 Binary files /dev/null and b/uploads/301622885258929.jpg differ diff --git a/uploads/371622885080660.jpg b/uploads/371622885080660.jpg new file mode 100644 index 0000000..0ddbbde Binary files /dev/null and b/uploads/371622885080660.jpg differ diff --git a/uploads/721622885064172.jpg b/uploads/721622885064172.jpg new file mode 100644 index 0000000..22affb6 Binary files /dev/null and b/uploads/721622885064172.jpg differ diff --git a/uploads/841622885280097.jpg b/uploads/841622885280097.jpg new file mode 100644 index 0000000..70599ea Binary files /dev/null and b/uploads/841622885280097.jpg differ diff --git a/uploads/911622885248542.jpg b/uploads/911622885248542.jpg new file mode 100644 index 0000000..b852aca Binary files /dev/null and b/uploads/911622885248542.jpg differ