├── .gitignore ├── README.md ├── backend ├── spring-boot-ecommerce │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── codecafe │ │ │ │ └── ecommerce │ │ │ │ ├── SpringBootEcommerceApplication.java │ │ │ │ └── persistence │ │ │ │ ├── config │ │ │ │ └── RepositoryRestConfig.java │ │ │ │ ├── dao │ │ │ │ ├── ProductCategoryRepository.java │ │ │ │ └── ProductRepository.java │ │ │ │ └── entity │ │ │ │ ├── Product.java │ │ │ │ └── ProductCategory.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── codecafe │ │ └── ecommerce │ │ └── SpringBootEcommerceApplicationTests.java └── sql-resources │ ├── 01-create-user.sql │ └── 02-create-products.sql └── frontend └── angular-ecommerce ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── components │ │ ├── cart-details │ │ │ ├── cart-details.component.css │ │ │ ├── cart-details.component.html │ │ │ ├── cart-details.component.spec.ts │ │ │ └── cart-details.component.ts │ │ ├── cart-status │ │ │ ├── cart-status.component.css │ │ │ ├── cart-status.component.html │ │ │ ├── cart-status.component.spec.ts │ │ │ └── cart-status.component.ts │ │ ├── checkout │ │ │ ├── checkout.component.css │ │ │ ├── checkout.component.html │ │ │ ├── checkout.component.spec.ts │ │ │ └── checkout.component.ts │ │ ├── product-category-menu │ │ │ ├── product-category-menu.component.css │ │ │ ├── product-category-menu.component.html │ │ │ ├── product-category-menu.component.spec.ts │ │ │ └── product-category-menu.component.ts │ │ ├── product-details │ │ │ ├── product-details.component.css │ │ │ ├── product-details.component.html │ │ │ ├── product-details.component.spec.ts │ │ │ └── product-details.component.ts │ │ ├── product-list │ │ │ ├── product-list.component.css │ │ │ ├── product-list.component.html │ │ │ ├── product-list.component.spec.ts │ │ │ └── product-list.component.ts │ │ └── search │ │ │ ├── search.component.css │ │ │ ├── search.component.html │ │ │ ├── search.component.spec.ts │ │ │ └── search.component.ts │ ├── model │ │ ├── cart-item.spec.ts │ │ ├── cart-item.ts │ │ ├── product-category.spec.ts │ │ ├── product-category.ts │ │ ├── product.spec.ts │ │ └── product.ts │ └── services │ │ ├── cart.service.spec.ts │ │ ├── cart.service.ts │ │ ├── product.service.spec.ts │ │ └── product.service.ts ├── assets │ ├── .gitkeep │ └── images │ │ ├── logo.png │ │ └── products │ │ ├── books │ │ ├── book-luv2code-1000.png │ │ ├── book-luv2code-1001.png │ │ ├── book-luv2code-1002.png │ │ ├── book-luv2code-1003.png │ │ ├── book-luv2code-1004.png │ │ ├── book-luv2code-1005.png │ │ ├── book-luv2code-1006.png │ │ ├── book-luv2code-1007.png │ │ ├── book-luv2code-1008.png │ │ ├── book-luv2code-1009.png │ │ ├── book-luv2code-1010.png │ │ ├── book-luv2code-1011.png │ │ ├── book-luv2code-1012.png │ │ ├── book-luv2code-1013.png │ │ ├── book-luv2code-1014.png │ │ ├── book-luv2code-1015.png │ │ ├── book-luv2code-1016.png │ │ ├── book-luv2code-1017.png │ │ ├── book-luv2code-1018.png │ │ ├── book-luv2code-1019.png │ │ ├── book-luv2code-1020.png │ │ ├── book-luv2code-1021.png │ │ ├── book-luv2code-1022.png │ │ ├── book-luv2code-1023.png │ │ └── book-luv2code-1024.png │ │ ├── coffeemugs │ │ ├── coffeemug-luv2code-1000.png │ │ ├── coffeemug-luv2code-1001.png │ │ ├── coffeemug-luv2code-1002.png │ │ ├── coffeemug-luv2code-1003.png │ │ ├── coffeemug-luv2code-1004.png │ │ ├── coffeemug-luv2code-1005.png │ │ ├── coffeemug-luv2code-1006.png │ │ ├── coffeemug-luv2code-1007.png │ │ ├── coffeemug-luv2code-1008.png │ │ ├── coffeemug-luv2code-1009.png │ │ ├── coffeemug-luv2code-1010.png │ │ ├── coffeemug-luv2code-1011.png │ │ ├── coffeemug-luv2code-1012.png │ │ ├── coffeemug-luv2code-1013.png │ │ ├── coffeemug-luv2code-1014.png │ │ ├── coffeemug-luv2code-1015.png │ │ ├── coffeemug-luv2code-1016.png │ │ ├── coffeemug-luv2code-1017.png │ │ ├── coffeemug-luv2code-1018.png │ │ ├── coffeemug-luv2code-1019.png │ │ ├── coffeemug-luv2code-1020.png │ │ ├── coffeemug-luv2code-1021.png │ │ ├── coffeemug-luv2code-1022.png │ │ ├── coffeemug-luv2code-1023.png │ │ └── coffeemug-luv2code-1024.png │ │ ├── luggagetags │ │ ├── luggagetag-luv2code-1000.png │ │ ├── luggagetag-luv2code-1001.png │ │ ├── luggagetag-luv2code-1002.png │ │ ├── luggagetag-luv2code-1003.png │ │ ├── luggagetag-luv2code-1004.png │ │ ├── luggagetag-luv2code-1005.png │ │ ├── luggagetag-luv2code-1006.png │ │ ├── luggagetag-luv2code-1007.png │ │ ├── luggagetag-luv2code-1008.png │ │ ├── luggagetag-luv2code-1009.png │ │ ├── luggagetag-luv2code-1010.png │ │ ├── luggagetag-luv2code-1011.png │ │ ├── luggagetag-luv2code-1012.png │ │ ├── luggagetag-luv2code-1013.png │ │ ├── luggagetag-luv2code-1014.png │ │ ├── luggagetag-luv2code-1015.png │ │ ├── luggagetag-luv2code-1016.png │ │ ├── luggagetag-luv2code-1017.png │ │ ├── luggagetag-luv2code-1018.png │ │ ├── luggagetag-luv2code-1019.png │ │ ├── luggagetag-luv2code-1020.png │ │ ├── luggagetag-luv2code-1021.png │ │ ├── luggagetag-luv2code-1022.png │ │ ├── luggagetag-luv2code-1023.png │ │ └── luggagetag-luv2code-1024.png │ │ ├── mousepads │ │ ├── mousepad-luv2code-1000.png │ │ ├── mousepad-luv2code-1001.png │ │ ├── mousepad-luv2code-1002.png │ │ ├── mousepad-luv2code-1003.png │ │ ├── mousepad-luv2code-1004.png │ │ ├── mousepad-luv2code-1005.png │ │ ├── mousepad-luv2code-1006.png │ │ ├── mousepad-luv2code-1007.png │ │ ├── mousepad-luv2code-1008.png │ │ ├── mousepad-luv2code-1009.png │ │ ├── mousepad-luv2code-1010.png │ │ ├── mousepad-luv2code-1011.png │ │ ├── mousepad-luv2code-1012.png │ │ ├── mousepad-luv2code-1013.png │ │ ├── mousepad-luv2code-1014.png │ │ ├── mousepad-luv2code-1015.png │ │ ├── mousepad-luv2code-1016.png │ │ ├── mousepad-luv2code-1017.png │ │ ├── mousepad-luv2code-1018.png │ │ ├── mousepad-luv2code-1019.png │ │ ├── mousepad-luv2code-1020.png │ │ ├── mousepad-luv2code-1021.png │ │ ├── mousepad-luv2code-1022.png │ │ ├── mousepad-luv2code-1023.png │ │ └── mousepad-luv2code-1024.png │ │ └── placeholder.png ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # full-stack-ecommerce-project 2 | A full stack e-commerce project with Angular (frontend), Spring-boot and MySQL (backend) 3 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/backend/spring-boot-ecommerce/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.7.RELEASE 9 | 10 | 11 | com.codecafe 12 | spring-boot-ecommerce 13 | 0.0.1-SNAPSHOT 14 | spring-boot-ecommerce 15 | Spring Boot E-Commerce Project 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-data-rest 29 | 30 | 31 | 32 | mysql 33 | mysql-connector-java 34 | runtime 35 | 36 | 37 | org.projectlombok 38 | lombok 39 | true 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.junit.vintage 48 | junit-vintage-engine 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-maven-plugin 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/java/com/codecafe/ecommerce/SpringBootEcommerceApplication.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootEcommerceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootEcommerceApplication.class, args); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/java/com/codecafe/ecommerce/persistence/config/RepositoryRestConfig.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce.persistence.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import javax.persistence.EntityManager; 8 | import javax.persistence.metamodel.EntityType; 9 | 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.data.rest.core.config.RepositoryRestConfiguration; 13 | import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; 14 | import org.springframework.http.HttpMethod; 15 | 16 | import com.codecafe.ecommerce.persistence.entity.Product; 17 | import com.codecafe.ecommerce.persistence.entity.ProductCategory; 18 | 19 | @Configuration 20 | public class RepositoryRestConfig implements RepositoryRestConfigurer { 21 | 22 | private EntityManager entityManager; 23 | 24 | // constructor injection - JPA Entity Manager 25 | @Autowired 26 | public RepositoryRestConfig(EntityManager entityManager) { 27 | this.entityManager = entityManager; 28 | } 29 | 30 | @Override 31 | public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { 32 | 33 | HttpMethod[] unsupportedHttpMethods = { HttpMethod.PUT, HttpMethod.POST, HttpMethod.DELETE }; 34 | 35 | // disable HTTP methods for Products: PUT, POST and DELETE 36 | config.getExposureConfiguration() 37 | .forDomainType(Product.class) 38 | .withItemExposure((metadata, httpMethods) -> httpMethods.disable(unsupportedHttpMethods)) 39 | .withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(unsupportedHttpMethods)); 40 | 41 | // disable HTTP methods for ProductCategory: PUT, POST and DELETE 42 | config.getExposureConfiguration() 43 | .forDomainType(ProductCategory.class) 44 | .withItemExposure((metadata, httpMethods) -> httpMethods.disable(unsupportedHttpMethods)) 45 | .withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(unsupportedHttpMethods)); 46 | 47 | // call an internal helper method 48 | exposeIds(config); 49 | } 50 | 51 | 52 | // entity ids (category ids) will be available in /api/product-category endpoint 53 | private void exposeIds(RepositoryRestConfiguration config) { 54 | // expose entity ids 55 | 56 | // get a list of all entity classes from the entity manager 57 | Set> entities = entityManager.getMetamodel().getEntities(); 58 | 59 | // create a list of the entity types 60 | List entityClasses = new ArrayList<>(); 61 | 62 | // get the entity types of the entities 63 | for(EntityType entityType : entities) { 64 | entityClasses.add(entityType.getJavaType()); 65 | } 66 | 67 | // expose the entity ids for the array of entity/domain types 68 | Class[] domainTypes = entityClasses.toArray(new Class[0]); 69 | config.exposeIdsFor(domainTypes); 70 | 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/java/com/codecafe/ecommerce/persistence/dao/ProductCategoryRepository.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce.persistence.dao; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | import org.springframework.web.bind.annotation.CrossOrigin; 6 | 7 | import com.codecafe.ecommerce.persistence.entity.ProductCategory; 8 | 9 | @CrossOrigin("http://localhost:4200") 10 | // collectionResourceRel - name of the JSON entry 11 | // path = /product-category 12 | @RepositoryRestResource(collectionResourceRel = "productCategory", path = "product-category") 13 | public interface ProductCategoryRepository extends JpaRepository { 14 | 15 | } -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/java/com/codecafe/ecommerce/persistence/dao/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce.persistence.dao; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | 9 | import com.codecafe.ecommerce.persistence.entity.Product; 10 | 11 | // Spring Data REST auto-magically creates SQL queries on the basis of the method names 12 | 13 | @CrossOrigin("http://localhost:4200") 14 | public interface ProductRepository extends JpaRepository { 15 | 16 | // Spring Data REST automatically exposes endpoint - http://host:port/api/products/search/findByCategoryId?id=:id 17 | 18 | // SELECT * FROM product WHERE category_id = ? 19 | Page findByCategoryId(@RequestParam("id") Long id, Pageable pageable); 20 | 21 | // SELECT * FROM product p WHERE p.name LIKE CONCAT('%', :name, '%') 22 | Page findByNameContaining(@RequestParam("name") String name, Pageable pageable); 23 | 24 | } -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/java/com/codecafe/ecommerce/persistence/entity/Product.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce.persistence.entity; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.GenerationType; 10 | import javax.persistence.Id; 11 | import javax.persistence.JoinColumn; 12 | import javax.persistence.ManyToOne; 13 | import javax.persistence.Table; 14 | 15 | import org.hibernate.annotations.CreationTimestamp; 16 | import org.hibernate.annotations.UpdateTimestamp; 17 | 18 | import lombok.Getter; 19 | import lombok.Setter; 20 | 21 | @Entity 22 | @Table(name="product") 23 | @Getter 24 | @Setter 25 | public class Product { 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | @Column(name = "id") 30 | private Long id; 31 | 32 | @ManyToOne 33 | @JoinColumn(name = "category_id", nullable = false) 34 | private ProductCategory category; 35 | 36 | @Column(name = "sku") 37 | private String sku; 38 | 39 | @Column(name = "name") 40 | private String name; 41 | 42 | @Column(name = "description") 43 | private String description; 44 | 45 | @Column(name = "unit_price") 46 | private BigDecimal unitPrice; 47 | 48 | @Column(name = "image_url") 49 | private String imageUrl; 50 | 51 | @Column(name = "active") 52 | private boolean active; 53 | 54 | @Column(name = "units_in_stock") 55 | private int unitsInStock; 56 | 57 | @Column(name = "date_created") 58 | @CreationTimestamp 59 | private Date dateCreated; 60 | 61 | @Column(name = "last_updated") 62 | @UpdateTimestamp 63 | private Date lastUpdated; 64 | 65 | } -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/java/com/codecafe/ecommerce/persistence/entity/ProductCategory.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce.persistence.entity; 2 | 3 | import java.util.Set; 4 | 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.GenerationType; 10 | import javax.persistence.Id; 11 | import javax.persistence.OneToMany; 12 | import javax.persistence.Table; 13 | 14 | import lombok.Getter; 15 | import lombok.Setter; 16 | 17 | @Entity 18 | @Table(name="product_category") 19 | @Getter 20 | @Setter 21 | public class ProductCategory { 22 | 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | @Column(name = "id") 26 | private Long id; 27 | 28 | @Column(name = "category_name") 29 | private String categoryName; 30 | 31 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "category") 32 | private Set products; 33 | 34 | } -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8090 2 | 3 | spring.data.rest.base-path=/api 4 | 5 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 6 | spring.datasource.url=jdbc:mysql://localhost:3306/full-stack-ecommerce?useSSL=false&useUnicode=yes&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTC 7 | spring.datasource.username=ecommerceapp 8 | spring.datasource.password=ecommerceapp 9 | 10 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -------------------------------------------------------------------------------- /backend/spring-boot-ecommerce/src/test/java/com/codecafe/ecommerce/SpringBootEcommerceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.codecafe.ecommerce; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootEcommerceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /backend/sql-resources/01-create-user.sql: -------------------------------------------------------------------------------- 1 | CREATE USER 'ecommerceapp'@'localhost' IDENTIFIED BY 'ecommerceapp'; 2 | 3 | GRANT ALL PRIVILEGES ON * . * TO 'ecommerceapp'@'localhost'; 4 | 5 | # 6 | # Starting with MySQL 8.0.4, the MySQL team changed the 7 | # default authentication plugin for MySQL server 8 | # from mysql_native_password to caching_sha2_password. 9 | # 10 | # The command below will make the appropriate updates for your user account. 11 | # 12 | # See the MySQL Reference Manual for details: 13 | # https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html 14 | # 15 | ALTER USER 'ecommerceapp'@'localhost' IDENTIFIED WITH mysql_native_password BY 'ecommerceapp'; -------------------------------------------------------------------------------- /frontend/angular-ecommerce/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/README.md: -------------------------------------------------------------------------------- 1 | # Angular E-Commerce 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.5. 4 | 5 | ## Development server 6 | 7 | Run `export NODE_OPTIONS=--openssl-legacy-provider` before running below command. 8 | 9 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 10 | 11 | ## Code scaffolding 12 | 13 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 14 | 15 | ## Build 16 | 17 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 18 | 19 | ## Running unit tests 20 | 21 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 22 | 23 | ## Running end-to-end tests 24 | 25 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 26 | 27 | ## Further help 28 | 29 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 30 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-ecommerce": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angular-ecommerce", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": true, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css", 28 | "node_modules/bootstrap/dist/css/bootstrap.min.css", 29 | "node_modules/@fortawesome/fontawesome-free/css/all.min.css" 30 | ], 31 | "scripts": [] 32 | }, 33 | "configurations": { 34 | "production": { 35 | "fileReplacements": [ 36 | { 37 | "replace": "src/environments/environment.ts", 38 | "with": "src/environments/environment.prod.ts" 39 | } 40 | ], 41 | "optimization": true, 42 | "outputHashing": "all", 43 | "sourceMap": false, 44 | "extractCss": true, 45 | "namedChunks": false, 46 | "extractLicenses": true, 47 | "vendorChunk": false, 48 | "buildOptimizer": true, 49 | "budgets": [ 50 | { 51 | "type": "initial", 52 | "maximumWarning": "2mb", 53 | "maximumError": "5mb" 54 | }, 55 | { 56 | "type": "anyComponentStyle", 57 | "maximumWarning": "6kb", 58 | "maximumError": "10kb" 59 | } 60 | ] 61 | } 62 | } 63 | }, 64 | "serve": { 65 | "builder": "@angular-devkit/build-angular:dev-server", 66 | "options": { 67 | "browserTarget": "angular-ecommerce:build" 68 | }, 69 | "configurations": { 70 | "production": { 71 | "browserTarget": "angular-ecommerce:build:production" 72 | } 73 | } 74 | }, 75 | "extract-i18n": { 76 | "builder": "@angular-devkit/build-angular:extract-i18n", 77 | "options": { 78 | "browserTarget": "angular-ecommerce:build" 79 | } 80 | }, 81 | "test": { 82 | "builder": "@angular-devkit/build-angular:karma", 83 | "options": { 84 | "main": "src/test.ts", 85 | "polyfills": "src/polyfills.ts", 86 | "tsConfig": "tsconfig.spec.json", 87 | "karmaConfig": "karma.conf.js", 88 | "assets": [ 89 | "src/favicon.ico", 90 | "src/assets" 91 | ], 92 | "styles": [ 93 | "src/styles.css" 94 | ], 95 | "scripts": [] 96 | } 97 | }, 98 | "lint": { 99 | "builder": "@angular-devkit/build-angular:tslint", 100 | "options": { 101 | "tsConfig": [ 102 | "tsconfig.app.json", 103 | "tsconfig.spec.json", 104 | "e2e/tsconfig.json" 105 | ], 106 | "exclude": [ 107 | "**/node_modules/**" 108 | ] 109 | } 110 | }, 111 | "e2e": { 112 | "builder": "@angular-devkit/build-angular:protractor", 113 | "options": { 114 | "protractorConfig": "e2e/protractor.conf.js", 115 | "devServerTarget": "angular-ecommerce:serve" 116 | }, 117 | "configurations": { 118 | "production": { 119 | "devServerTarget": "angular-ecommerce:serve:production" 120 | } 121 | } 122 | } 123 | } 124 | }}, 125 | "defaultProject": "angular-ecommerce", 126 | "cli": { 127 | "analytics": false 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /frontend/angular-ecommerce/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /frontend/angular-ecommerce/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('angular-ecommerce app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/angular-ecommerce'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ecommerce", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~9.1.13", 15 | "@angular/common": "~9.1.13", 16 | "@angular/compiler": "~9.1.13", 17 | "@angular/core": "~9.1.13", 18 | "@angular/forms": "~9.1.13", 19 | "@angular/localize": "^9.1.13", 20 | "@angular/platform-browser": "~9.1.13", 21 | "@angular/platform-browser-dynamic": "~9.1.13", 22 | "@angular/router": "~9.1.13", 23 | "@fortawesome/fontawesome-free": "^5.13.0", 24 | "@ng-bootstrap/ng-bootstrap": "^6.1.0", 25 | "bootstrap": "^4.4.1", 26 | "rxjs": "~6.5.4", 27 | "tslib": "^1.10.0", 28 | "zone.js": "~0.10.2" 29 | }, 30 | "devDependencies": { 31 | "@angular-devkit/build-angular": "~0.901.15", 32 | "@angular/cli": "~9.1.15", 33 | "@angular/compiler-cli": "~9.1.13", 34 | "@types/node": "^12.11.1", 35 | "@types/jasmine": "~3.5.0", 36 | "@types/jasminewd2": "~2.0.3", 37 | "codelyzer": "^5.1.2", 38 | "jasmine-core": "~4.5.0", 39 | "jasmine-spec-reporter": "~4.2.1", 40 | "karma": "~6.4.2", 41 | "karma-chrome-launcher": "~3.1.0", 42 | "karma-coverage-istanbul-reporter": "~2.1.0", 43 | "karma-jasmine": "~3.0.1", 44 | "karma-jasmine-html-reporter": "^1.4.2", 45 | "protractor": "~5.4.3", 46 | "ts-node": "~8.3.0", 47 | "tslint": "~6.1.0", 48 | "typescript": "~3.8.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/app.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 | 27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'angular-ecommerce'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('angular-ecommerce'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement; 29 | expect(compiled.querySelector('.content span').textContent).toContain('angular-ecommerce app is running!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'angular-ecommerce'; 10 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { ProductListComponent } from './components/product-list/product-list.component'; 6 | 7 | import { HttpClientModule } from '@angular/common/http' 8 | import { ProductService } from './services/product.service'; 9 | 10 | import { Routes, RouterModule } from '@angular/router'; 11 | import { ProductCategoryMenuComponent } from './components/product-category-menu/product-category-menu.component'; 12 | import { SearchComponent } from './components/search/search.component'; 13 | import { ProductDetailsComponent } from './components/product-details/product-details.component'; 14 | 15 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 16 | import { CartStatusComponent } from './components/cart-status/cart-status.component'; 17 | import { CartDetailsComponent } from './components/cart-details/cart-details.component'; 18 | import { CheckoutComponent } from './components/checkout/checkout.component' 19 | import { ReactiveFormsModule } from '@angular/forms'; 20 | 21 | const routes: Routes = [ 22 | { path: 'checkout', component: CheckoutComponent }, 23 | { path: 'cart-details', component: CartDetailsComponent }, 24 | { path: 'products/:id', component: ProductDetailsComponent }, 25 | { path: 'search/:keyword', component: ProductListComponent }, 26 | { path: 'category/:id', component: ProductListComponent }, 27 | { path: 'category', component: ProductListComponent }, 28 | { path: 'products', component: ProductListComponent }, 29 | { path: '', redirectTo: '/products', pathMatch: 'full' }, 30 | { path: '**', redirectTo: '/products', pathMatch: 'full' }, 31 | ]; 32 | 33 | @NgModule({ 34 | declarations: [ 35 | AppComponent, 36 | ProductListComponent, 37 | ProductCategoryMenuComponent, 38 | SearchComponent, 39 | ProductDetailsComponent, 40 | CartStatusComponent, 41 | CartDetailsComponent, 42 | CheckoutComponent 43 | ], 44 | imports: [ 45 | RouterModule.forRoot(routes), 46 | BrowserModule, 47 | HttpClientModule, 48 | NgbModule, 49 | ReactiveFormsModule 50 | ], 51 | providers: [ProductService], 52 | bootstrap: [AppComponent] 53 | }) 54 | export class AppModule { } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-details/cart-details.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/cart-details/cart-details.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-details/cart-details.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 20 | 51 | 52 | 53 | 54 | 55 | 61 | 62 |
Product ImageProduct Details
14 | 15 | 17 |

{{ cartItem.name }}

18 |

{{ cartItem.unitPrice | currency: 'USD'}}

19 |
21 |
22 | 23 | 24 |
25 | 26 |
27 | 30 |
31 | 32 |
33 | {{ cartItem.quantity }} 34 |
35 | 36 |
37 | 40 |
41 | 42 |
43 |
44 | 45 |
46 | 47 | 48 | 49 |

SubTotal: {{ cartItem.quantity * cartItem.unitPrice | currency: 'USD' }}

50 |
56 |

Total Quantity: {{ totalQuantity }}

57 |

Shipping: FREE

58 |

Total Price: {{ totalPrice | currency: 'USD' }}

59 | Checkout 60 |
63 |
64 | 65 | 66 | 69 | 70 |
71 |
72 |
-------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-details/cart-details.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CartDetailsComponent } from './cart-details.component'; 4 | 5 | describe('CartDetailsComponent', () => { 6 | let component: CartDetailsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CartDetailsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CartDetailsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-details/cart-details.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CartItem } from 'src/app/model/cart-item'; 3 | import { CartService } from 'src/app/services/cart.service'; 4 | import { ThrowStmt } from '@angular/compiler'; 5 | 6 | @Component({ 7 | selector: 'app-cart-details', 8 | templateUrl: './cart-details.component.html', 9 | styleUrls: ['./cart-details.component.css'] 10 | }) 11 | export class CartDetailsComponent implements OnInit { 12 | 13 | cartItems: CartItem[] = []; 14 | totalPrice: number = 0.00; 15 | totalQuantity: number = 0; 16 | 17 | 18 | constructor(private cartService: CartService) { } 19 | 20 | 21 | ngOnInit(): void { 22 | this.listCartDetails(); 23 | } 24 | 25 | 26 | listCartDetails() { 27 | 28 | // get a handle to to the cart items 29 | this.cartItems = this.cartService.cartItems; 30 | 31 | // subscribe to the cart totalPrice event 32 | this.cartService.totalPrice.subscribe( 33 | data => this.totalPrice = data 34 | ); 35 | 36 | // subscribe to the cart totalQuantity event 37 | this.cartService.totalQuantity.subscribe( 38 | data => this.totalQuantity = data 39 | ); 40 | 41 | // compute cart total price and quantity 42 | this.cartService.computeCartTotals(); 43 | 44 | } 45 | 46 | 47 | incrementQuantity(cartItem: CartItem) { 48 | this.cartService.addToCart(cartItem); 49 | } 50 | 51 | 52 | decrementQuantity(cartItem: CartItem) { 53 | this.cartService.decrementQuantity(cartItem); 54 | } 55 | 56 | 57 | remove(cartItem: CartItem) { 58 | this.cartService.remove(cartItem); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-status/cart-status.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/cart-status/cart-status.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-status/cart-status.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-status/cart-status.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CartStatusComponent } from './cart-status.component'; 4 | 5 | describe('CartStatusComponent', () => { 6 | let component: CartStatusComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CartStatusComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CartStatusComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/cart-status/cart-status.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CartService } from 'src/app/services/cart.service'; 3 | 4 | @Component({ 5 | selector: 'app-cart-status', 6 | templateUrl: './cart-status.component.html', 7 | styleUrls: ['./cart-status.component.css'] 8 | }) 9 | export class CartStatusComponent implements OnInit { 10 | 11 | totalPrice: number = 0.00; 12 | totalQuantity: number = 0; 13 | 14 | 15 | constructor(private cartService: CartService) { } 16 | 17 | 18 | ngOnInit(): void { 19 | this.updateCartStatus(); 20 | } 21 | 22 | 23 | updateCartStatus() { 24 | 25 | // subscribe to the cart totalPrice event 26 | this.cartService.totalPrice.subscribe( 27 | data => this.totalPrice = data 28 | ); 29 | 30 | // subscribe to the cart totalQuantity event 31 | this.cartService.totalQuantity.subscribe( 32 | data => this.totalQuantity = data 33 | ); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/checkout/checkout.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/checkout/checkout.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/checkout/checkout.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 | 8 |
9 |

Customer

10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |

Shipping Address

39 |
40 |
41 |
42 |
43 | 46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | 72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | 80 |
81 |
82 |
83 |
84 | 85 | 86 |
87 | 91 |
92 | 93 | 94 |
95 |

Billing Address

96 |
97 |
98 |
99 |
100 | 103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | 111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | 119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | 129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | 137 |
138 |
139 |
140 |
141 | 142 | 143 |
144 |

Credit Card

145 |
146 |
147 |
148 |
149 | 153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | 161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | 169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 | 177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | 187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | 197 |
198 |
199 |
200 |
201 | 202 | 203 |
204 |

Review Your Order

205 | 206 |

Total Quantity: {{ totalQuantity }}

207 |

Total Price: {{ totalPrice | currency: 'USD' }}

208 |
209 | 210 | 211 |
212 | 213 |
214 | 215 |
216 | 217 |
218 |
219 |
-------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/checkout/checkout.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CheckoutComponent } from './checkout.component'; 4 | 5 | describe('CheckoutComponent', () => { 6 | let component: CheckoutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CheckoutComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CheckoutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/checkout/checkout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, FormBuilder } from '@angular/forms'; 3 | 4 | @Component({ 5 | selector: 'app-checkout', 6 | templateUrl: './checkout.component.html', 7 | styleUrls: ['./checkout.component.css'] 8 | }) 9 | export class CheckoutComponent implements OnInit { 10 | 11 | checkoutFormGroup: FormGroup; 12 | 13 | totalPrice: number = 0.00; 14 | totalQuantity: number = 0; 15 | 16 | constructor(private formBuilder: FormBuilder) { } 17 | 18 | ngOnInit(): void { 19 | 20 | this.checkoutFormGroup = this.formBuilder.group({ 21 | customer: this.formBuilder.group({ 22 | firstName: [''], 23 | lastName: [''], 24 | email: [''] 25 | }), 26 | shippingAddress: this.formBuilder.group({ 27 | street: [''], 28 | city: [''], 29 | state: [''], 30 | country: [''], 31 | zipCode: [''] 32 | }), 33 | billingAddress: this.formBuilder.group({ 34 | street: [''], 35 | city: [''], 36 | state: [''], 37 | country: [''], 38 | zipCode: [''] 39 | }), 40 | creditCard: this.formBuilder.group({ 41 | cardType: [''], 42 | nameOnCard: [''], 43 | cardNumber: [''], 44 | securityCode: [''], 45 | expirationMonth: [''], 46 | expirationYear: [''] 47 | }) 48 | }); 49 | } 50 | 51 | 52 | onSubmit() { 53 | console.log("Handling the submit button"); 54 | console.log(this.checkoutFormGroup.get('customer').value); 55 | //console.log("The email address is " + this.checkoutFormGroup.get('customer').value.email); 56 | } 57 | 58 | 59 | copyShippingAddressToBillingAddress(event) { 60 | 61 | if(event.target.checked) { 62 | this.checkoutFormGroup.controls.billingAddress.setValue(this.checkoutFormGroup.controls.shippingAddress.value); 63 | } else { 64 | this.checkoutFormGroup.controls.billingAddress.reset(); 65 | } 66 | 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-category-menu/product-category-menu.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/product-category-menu/product-category-menu.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-category-menu/product-category-menu.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-category-menu/product-category-menu.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProductCategoryMenuComponent } from './product-category-menu.component'; 4 | 5 | describe('ProductCategoryMenuComponent', () => { 6 | let component: ProductCategoryMenuComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ProductCategoryMenuComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ProductCategoryMenuComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-category-menu/product-category-menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ProductCategory } from 'src/app/model/product-category'; 3 | import { ProductService } from 'src/app/services/product.service'; 4 | 5 | @Component({ 6 | selector: 'app-product-category-menu', 7 | templateUrl: './product-category-menu.component.html', 8 | styleUrls: ['./product-category-menu.component.css'] 9 | }) 10 | export class ProductCategoryMenuComponent implements OnInit { 11 | 12 | productCategories: ProductCategory[]; 13 | 14 | constructor(private productService: ProductService) { } 15 | 16 | ngOnInit(): void { 17 | this.listProductCategories(); 18 | } 19 | 20 | listProductCategories() { 21 | this.productService.getProductCategories().subscribe( 22 | data => { 23 | console.log("Product Categories : " + JSON.stringify(data)); 24 | this.productCategories = data; 25 | } 26 | ) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-details/product-details.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/product-details/product-details.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-details/product-details.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |

{{ product.name }}

7 |
{{ product.unitPrice | currency: "USD" }}
8 | 9 | 10 |
11 |

Description

12 |

{{ product.description }}

13 | 14 | Back to Product List 15 |
16 |
-------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-details/product-details.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProductDetailsComponent } from './product-details.component'; 4 | 5 | describe('ProductDetailsComponent', () => { 6 | let component: ProductDetailsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ProductDetailsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ProductDetailsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-details/product-details.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Product } from 'src/app/model/product'; 3 | import { ProductService } from 'src/app/services/product.service'; 4 | import { ActivatedRoute } from '@angular/router'; 5 | import { CartService } from 'src/app/services/cart.service'; 6 | import { CartItem } from 'src/app/model/cart-item'; 7 | 8 | @Component({ 9 | selector: 'app-product-details', 10 | templateUrl: './product-details.component.html', 11 | styleUrls: ['./product-details.component.css'] 12 | }) 13 | export class ProductDetailsComponent implements OnInit { 14 | 15 | product: Product = new Product(); 16 | 17 | constructor(private productService: ProductService, private cartService: CartService, private route: ActivatedRoute) { } 18 | 19 | ngOnInit(): void { 20 | this.route.paramMap.subscribe(() => this.handleProductDetails()); 21 | } 22 | 23 | handleProductDetails(): void { 24 | 25 | // get the "id" param string and convert it to number using the "+" symbol 26 | const productId: number = +this.route.snapshot.paramMap.get("id"); 27 | 28 | this.productService.getProduct(productId).subscribe( 29 | data => { 30 | this.product = data; 31 | } 32 | ); 33 | } 34 | 35 | addToCart(product: Product) { 36 | console.log(`Adding to cart: ${product.name}, ${product.unitPrice}`); 37 | const theCartItem = new CartItem(product); 38 | this.cartService.addToCart(theCartItem); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-list/product-list.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/product-list/product-list.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-list/product-list.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |

{{ product.name }}

18 |
19 | 20 |
{{ product.unitPrice | currency: "USD" }}
21 | 22 |
23 | 24 |
25 | 26 | 27 | 30 | 31 |
32 | 33 | 34 | 63 | 64 | 65 |
66 |
67 |
-------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-list/product-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProductListComponent } from './product-list.component'; 4 | 5 | describe('ProductListComponent', () => { 6 | let component: ProductListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ProductListComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ProductListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/product-list/product-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Product } from 'src/app/model/product'; 3 | import { ProductService } from 'src/app/services/product.service'; 4 | import { ActivatedRoute } from '@angular/router'; 5 | import { CartItem } from 'src/app/model/cart-item'; 6 | import { CartService } from 'src/app/services/cart.service'; 7 | 8 | @Component({ 9 | selector: 'app-product-list', 10 | templateUrl: './product-list.component.html', 11 | styleUrls: ['./product-list.component.css'] 12 | }) 13 | export class ProductListComponent implements OnInit { 14 | 15 | products: Product[]; 16 | currentCategoryId: number = 1; 17 | previousCategoryId: number = 1; 18 | searchMode: boolean = false; 19 | 20 | // new properties for pagination 21 | pageNumber: number = 1; 22 | pageSize: number = 5; 23 | totalElements: number = 0; 24 | 25 | previousKeyword: string = null; 26 | 27 | constructor(private productService: ProductService, private cartService: CartService, private route: ActivatedRoute) { } 28 | 29 | 30 | // similar to @PostConstruct of Spring 31 | ngOnInit(): void { 32 | this.route.paramMap.subscribe(() => this.listProducts()); 33 | } 34 | 35 | 36 | listProducts() { 37 | 38 | // if url has the "keyword" query parameter that means it is a search mode 39 | this.searchMode = this.route.snapshot.paramMap.has("keyword"); 40 | 41 | if (this.searchMode) { 42 | this.handleSearchProducts(); 43 | } else { 44 | this.handleListProducts(); 45 | } 46 | 47 | } 48 | 49 | 50 | handleSearchProducts() { 51 | 52 | const searchKeyword: string = this.route.snapshot.paramMap.get("keyword"); 53 | 54 | // if we have different keyword than previous then set the pageNumber to 1 55 | if(this.previousKeyword != searchKeyword) { 56 | this.pageNumber = 1; 57 | } 58 | 59 | this.previousKeyword = searchKeyword; 60 | 61 | console.log(`searchKeyword=${searchKeyword}, pageNumber=${this.pageNumber}`); 62 | 63 | // now search for the products using the searchKeyword 64 | this.productService.searchProductsPaginated(this.pageNumber - 1, this.pageSize, searchKeyword).subscribe(this.processResult()); 65 | 66 | } 67 | 68 | 69 | handleListProducts() { 70 | // check if "id" parameter is available 71 | const hasCategoryId: boolean = this.route.snapshot.paramMap.has("id"); 72 | 73 | if (hasCategoryId) { 74 | // get the "id" param string. convert string to a number using the "+" symbol 75 | this.currentCategoryId = +this.route.snapshot.paramMap.get("id"); 76 | } else { 77 | // no category id available .. default to category id 1 78 | this.currentCategoryId = 1; 79 | } 80 | 81 | // check if we have a different category id than previous 82 | // Note : Angular will re-use a component if it is currently being used in the browser 83 | 84 | // if we have a different category id than the previous then set the pageNumber back to 1 85 | if(this.previousCategoryId != this.currentCategoryId) { 86 | this.pageNumber = 1; 87 | } 88 | 89 | this.previousCategoryId = this.currentCategoryId; 90 | console.log(`currentCategoryId=${this.currentCategoryId}, pageNumber=${this.pageNumber}`); 91 | 92 | // now get the products for the given category id 93 | this.productService.getProductListPaginated(this.pageNumber - 1, this.pageSize, this.currentCategoryId).subscribe(this.processResult()); 94 | 95 | } 96 | 97 | 98 | processResult() { 99 | return data => { 100 | this.products = data._embedded.products; 101 | this.pageNumber = data.page.number + 1; // Spring Data REST: pages are 0 based - Angular: pages are 1 based 102 | this.pageSize = data.page.size; 103 | this.totalElements = data.page.totalElements; 104 | }; 105 | } 106 | 107 | 108 | updatePageSize(updatedPageSize: number) { 109 | this.pageSize = updatedPageSize; 110 | this.pageNumber = 1; 111 | this.listProducts(); 112 | } 113 | 114 | 115 | addToCart(product: Product) { 116 | 117 | console.log(`Adding to cart: ${product.name}, ${product.unitPrice}`); 118 | 119 | // do the real work 120 | const theCartItem = new CartItem(product); 121 | 122 | this.cartService.addToCart(theCartItem); 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/search/search.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/app/components/search/search.component.css -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/search/search.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 | 11 | 12 |
-------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/search/search.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SearchComponent } from './search.component'; 4 | 5 | describe('SearchComponent', () => { 6 | let component: SearchComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SearchComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SearchComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/components/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-search', 6 | templateUrl: './search.component.html', 7 | styleUrls: ['./search.component.css'] 8 | }) 9 | export class SearchComponent implements OnInit { 10 | 11 | constructor(private router: Router) { } 12 | 13 | ngOnInit(): void { } 14 | 15 | doSearch(searchString: string) { 16 | console.log(`searchString=${searchString}`); 17 | this.router.navigateByUrl(`search/${searchString}`); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/model/cart-item.spec.ts: -------------------------------------------------------------------------------- 1 | import { CartItem } from './cart-item'; 2 | 3 | describe('CartItem', () => { 4 | it('should create an instance', () => { 5 | expect(new CartItem()).toBeTruthy(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/model/cart-item.ts: -------------------------------------------------------------------------------- 1 | import { Product } from './product'; 2 | 3 | export class CartItem { 4 | 5 | id: string; 6 | name: string; 7 | imageUrl: string; 8 | unitPrice: number; 9 | 10 | quantity: number; 11 | 12 | constructor(product: Product) { 13 | this.id = product.id; 14 | this.name = product.name; 15 | this.imageUrl = product.imageUrl; 16 | this.unitPrice = product.unitPrice; 17 | 18 | this.quantity = 1; 19 | } 20 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/model/product-category.spec.ts: -------------------------------------------------------------------------------- 1 | import { ProductCategory } from './product-category'; 2 | 3 | describe('ProductCategory', () => { 4 | it('should create an instance', () => { 5 | expect(new ProductCategory()).toBeTruthy(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/model/product-category.ts: -------------------------------------------------------------------------------- 1 | export class ProductCategory { 2 | 3 | id: number; 4 | categoryName: string; 5 | 6 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/model/product.spec.ts: -------------------------------------------------------------------------------- 1 | import { Product } from './product'; 2 | 3 | describe('Product', () => { 4 | it('should create an instance', () => { 5 | expect(new Product()).toBeTruthy(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/model/product.ts: -------------------------------------------------------------------------------- 1 | export class Product { 2 | id: string; 3 | sku: string; 4 | name: string; 5 | description: string; 6 | unitPrice: number; 7 | imageUrl: string; 8 | active: boolean; 9 | unitsInStock: number; 10 | dateCreated: Date; 11 | lastUpdated: Date; 12 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/services/cart.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CartService } from './cart.service'; 4 | 5 | describe('CartService', () => { 6 | let service: CartService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CartService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/services/cart.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CartItem } from '../model/cart-item'; 3 | import { Subject } from 'rxjs'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class CartService { 9 | 10 | cartItems: CartItem[] = []; 11 | 12 | // Subject is a subclass of Observable. We can use Subject to publish events. The event will be sent to all of the subscribers. 13 | totalPrice: Subject = new Subject(); 14 | totalQuantity: Subject = new Subject(); 15 | 16 | 17 | constructor() { } 18 | 19 | 20 | addToCart(theCartItem: CartItem) { 21 | 22 | // check if we already have the item in our cart 23 | let alreadyExistsInCart: boolean = false; 24 | let existingCartItem: CartItem = undefined; 25 | 26 | if (this.cartItems.length > 0) { 27 | 28 | // find the item in the cart based on the item id 29 | existingCartItem = this.cartItems.find(cartItem => cartItem.id === theCartItem.id); 30 | 31 | // check if we found it 32 | alreadyExistsInCart = (existingCartItem != undefined); 33 | 34 | } 35 | 36 | if (alreadyExistsInCart) { 37 | // increment the quantity 38 | existingCartItem.quantity++; 39 | } else { 40 | // just add the item to the array 41 | this.cartItems.push(theCartItem); 42 | } 43 | 44 | // compute cart total price and total quantity 45 | this.computeCartTotals(); 46 | 47 | } 48 | 49 | 50 | computeCartTotals() { 51 | 52 | let totalPriceValue: number = 0; 53 | let totalQuantityValue: number = 0; 54 | 55 | for (let currentCartItem of this.cartItems) { 56 | totalPriceValue += currentCartItem.quantity * currentCartItem.unitPrice; 57 | totalQuantityValue += currentCartItem.quantity; 58 | } 59 | 60 | // publish the new values, all subscribers will receive the new data 61 | 62 | // this will publish two events to all subscribers 63 | // one event for totalPrice, one event for totalQuantity 64 | this.totalPrice.next(totalPriceValue); 65 | this.totalQuantity.next(totalQuantityValue); 66 | 67 | // log the cart data for debugging purposes 68 | this.logCartData(totalPriceValue, totalQuantityValue); 69 | } 70 | 71 | 72 | logCartData(totalPriceValue: number, totalQuantityValue: number) { 73 | 74 | console.log("Contents of the cart"); 75 | for (let cartItem of this.cartItems) { 76 | const subTotalPrice = cartItem.quantity * cartItem.unitPrice; 77 | console.log(`name: ${cartItem.name}, quantity: ${cartItem.quantity}, unitPrice: ${cartItem.unitPrice}, subTotalPrice: ${subTotalPrice}`); 78 | } 79 | 80 | console.log(`totalPrice: ${totalPriceValue.toFixed(2)}, totalQuantity: ${totalQuantityValue}`); 81 | console.log("----"); 82 | } 83 | 84 | 85 | decrementQuantity(cartItem: CartItem) { 86 | cartItem.quantity--; 87 | 88 | if(cartItem.quantity === 0) { 89 | this.remove(cartItem); 90 | } else { 91 | this.computeCartTotals(); 92 | } 93 | 94 | } 95 | 96 | 97 | remove(cartItem: CartItem) { 98 | 99 | // get index of the item in the array 100 | const itemIndex = this.cartItems.findIndex(tempCartItem => tempCartItem.id === cartItem.id); 101 | 102 | // if found, remove the item from the array at the given index 103 | if(itemIndex > -1) { 104 | this.cartItems.splice(itemIndex, 1); 105 | 106 | this.computeCartTotals(); 107 | } 108 | 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/services/product.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ProductService } from './product.service'; 4 | 5 | describe('ProductService', () => { 6 | let service: ProductService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ProductService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/app/services/product.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import { Product } from '../model/product'; 6 | import { ProductCategory } from '../model/product-category'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class ProductService { 12 | 13 | private baseUrl = "http://localhost:8090/api/products"; 14 | 15 | private categoryUrl = "http://localhost:8090/api/product-category"; 16 | 17 | 18 | // httpClient will be injected automatically by Angular's dependency injection 19 | constructor(private httpClient: HttpClient) { } 20 | 21 | 22 | getProduct(productId: number): Observable { 23 | 24 | // need to build the URL based on the product id 25 | const productUrl = `${this.baseUrl}/${productId}` 26 | 27 | return this.httpClient.get(productUrl); 28 | } 29 | 30 | 31 | getProductList(categoryId: number): Observable { 32 | 33 | // need to build the URL based on the category id 34 | const searchUrl = `${this.baseUrl}/search/findByCategoryId?id=${categoryId}` 35 | 36 | return this.getProducts(searchUrl); 37 | 38 | } 39 | 40 | 41 | getProductListPaginated(page: number, pageSize: number, categoryId: number): Observable { 42 | 43 | // need to build the URL based on the category id, page and size 44 | const searchUrl = `${this.baseUrl}/search/findByCategoryId?id=${categoryId}` + `&page=${page}&size=${pageSize}`; 45 | 46 | return this.httpClient.get(searchUrl); 47 | 48 | } 49 | 50 | 51 | getProductCategories(): Observable { 52 | 53 | return this.httpClient.get(this.categoryUrl).pipe( 54 | map(response => response._embedded.productCategory) 55 | ); 56 | 57 | } 58 | 59 | 60 | searchProducts(searchKeyword: string): Observable { 61 | 62 | // need to build the URL based on the search keyword 63 | const searchUrl = `${this.baseUrl}/search/findByNameContaining?name=${searchKeyword}` 64 | 65 | return this.getProducts(searchUrl); 66 | 67 | } 68 | 69 | 70 | searchProductsPaginated(page: number, pageSize: number, searchKeyword: string): Observable { 71 | 72 | // need to build the URL based on the search keyword, page and size 73 | const searchUrl = `${this.baseUrl}/search/findByNameContaining?name=${searchKeyword}&page=${page}&size=${pageSize}`; 74 | 75 | return this.httpClient.get(searchUrl); 76 | 77 | } 78 | 79 | 80 | private getProducts(searchUrl: string): Observable { 81 | 82 | return this.httpClient.get(searchUrl).pipe( 83 | map(response => response._embedded.products) 84 | ); 85 | 86 | } 87 | 88 | } 89 | 90 | 91 | interface GetResponseProducts { 92 | // unwraps the JSON from Spring Data REST _embedded entry 93 | _embedded: { 94 | products: Product[]; 95 | }, 96 | page: { 97 | size: number, 98 | totalElements: number, 99 | totalPages: number, 100 | number: number 101 | } 102 | } 103 | 104 | 105 | interface GetResponseProductCategory { 106 | // unwraps the JSON from Spring Data REST _embedded entry 107 | _embedded: { 108 | productCategory: ProductCategory[]; 109 | } 110 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/logo.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1000.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1001.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1002.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1003.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1004.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1005.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1006.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1007.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1008.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1009.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1010.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1011.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1012.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1013.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1014.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1015.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1016.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1017.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1018.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1019.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1020.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1021.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1022.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1023.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/books/book-luv2code-1024.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1000.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1001.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1002.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1003.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1004.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1005.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1006.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1007.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1008.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1009.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1010.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1011.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1012.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1013.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1014.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1015.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1016.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1017.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1018.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1019.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1020.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1021.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1022.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1023.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/coffeemugs/coffeemug-luv2code-1024.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1000.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1001.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1002.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1003.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1004.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1005.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1006.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1007.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1008.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1009.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1010.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1011.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1012.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1013.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1014.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1015.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1016.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1017.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1018.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1019.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1020.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1021.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1022.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1023.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/luggagetags/luggagetag-luv2code-1024.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1000.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1001.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1002.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1003.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1004.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1005.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1006.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1007.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1008.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1009.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1010.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1011.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1012.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1013.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1014.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1015.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1016.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1017.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1018.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1019.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1020.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1021.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1022.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1023.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/mousepads/mousepad-luv2code-1024.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/assets/images/products/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/assets/images/products/placeholder.png -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-nath/full-stack-ecommerce-project/0376dc8a4973522d1f07ebf76801d42c45ee3aa3/frontend/angular-ecommerce/src/favicon.ico -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | luv2shop - Full Stack eCommerce: Angular and Spring Boot 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. 3 | */ 4 | import '@angular/localize/init'; 5 | /** 6 | * This file includes polyfills needed by Angular and is loaded before the app. 7 | * You can add your own extra polyfills to this file. 8 | * 9 | * This file is divided into 2 sections: 10 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 11 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 12 | * file. 13 | * 14 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 15 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 16 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 17 | * 18 | * Learn more in https://angular.io/guide/browser-support 19 | */ 20 | 21 | /*************************************************************************************************** 22 | * BROWSER POLYFILLS 23 | */ 24 | 25 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 26 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 27 | 28 | /** 29 | * Web Animations `@angular/platform-browser/animations` 30 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 31 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 32 | */ 33 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 34 | 35 | /** 36 | * By default, zone.js will patch all possible macroTask and DomEvents 37 | * user can disable parts of macroTask/DomEvents patch by setting following flags 38 | * because those flags need to be set before `zone.js` being loaded, and webpack 39 | * will put import in the top of bundle, so user need to create a separate file 40 | * in this directory (for example: zone-flags.ts), and put the following flags 41 | * into that file, and then add the following code before importing zone.js. 42 | * import './zone-flags'; 43 | * 44 | * The flags allowed in zone-flags.ts are listed here. 45 | * 46 | * The following flags will work for all browsers. 47 | * 48 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 49 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 50 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 51 | * 52 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 53 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 54 | * 55 | * (window as any).__Zone_enable_cross_context_check = true; 56 | * 57 | */ 58 | 59 | /*************************************************************************************************** 60 | * Zone JS is required by default for Angular itself. 61 | */ 62 | import 'zone.js/dist/zone'; // Included with Angular CLI. 63 | 64 | 65 | /*************************************************************************************************** 66 | * APPLICATION IMPORTS 67 | */ 68 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | * ---------------------------- 3 | * Common CSS 4 | * ---------------------------- 5 | */ 6 | 7 | *{ 8 | margin:0; 9 | padding:0; 10 | -webkit-box-sizing:border-box; 11 | -moz-box-sizing:border-box; 12 | box-sizing:border-box 13 | } 14 | 15 | body{ 16 | font-family:Poppins,sans-serif; 17 | font-weight:400; 18 | font-size:16px; 19 | line-height:1.625; 20 | color:#666; 21 | -webkit-font-smoothing:antialiased; 22 | -moz-osx-font-smoothing:grayscale 23 | } 24 | 25 | ul{ 26 | margin:0 27 | } 28 | 29 | button,input[type=button]{ 30 | cursor:pointer 31 | } 32 | 33 | button:focus,input:focus{ 34 | outline:0 35 | } 36 | 37 | input{ 38 | border:none 39 | } 40 | 41 | button{ 42 | border:none; 43 | background:0 0 44 | } 45 | 46 | img{ 47 | max-width:100%; 48 | height:auto 49 | } 50 | 51 | p{ 52 | margin:0 53 | } 54 | 55 | h1,h2,h3,h4,h5,h6{ 56 | color:#333; 57 | font-weight:700; 58 | margin:0; 59 | line-height:1.2 60 | } 61 | 62 | h1{ 63 | font-size:36px 64 | } 65 | 66 | h2{ 67 | font-size:30px 68 | } 69 | 70 | h3{ 71 | font-size:24px 72 | } 73 | 74 | h4{ 75 | font-size:18px 76 | } 77 | 78 | h5{ 79 | font-size:15px 80 | } 81 | 82 | h6{ 83 | font-size:13px 84 | } 85 | 86 | a{ 87 | display:inline-block 88 | } 89 | 90 | a:active,a:focus,a:hover{ 91 | text-decoration:none; 92 | outline:0 93 | } 94 | 95 | a,a:hover{ 96 | -webkit-transition:all .3s ease; 97 | -o-transition:all .3s ease; 98 | -moz-transition:all .3s ease; 99 | transition:all .3s ease 100 | } 101 | 102 | /*Layout Css*/ 103 | .section-content-p35{ 104 | padding:0 35px 105 | } 106 | 107 | .page-wrapper{ 108 | overflow:hidden; 109 | background:#e5e5e5; 110 | padding-bottom:8vh 111 | } 112 | 113 | .page-container{ 114 | background:#e5e5e5; 115 | padding-left:200px 116 | } 117 | 118 | .main-content{ 119 | padding-top:116px; 120 | min-height:100vh 121 | } 122 | 123 | aside.menu-sidebar.d-none.d-lg-block.clr{ 124 | background:0 0; 125 | z-index:99999999; 126 | height:75px; 127 | box-shadow:-2px 0 4px #0000003b 128 | } 129 | 130 | /* 131 | * ---------------------------- 132 | * Header Desktop Section 133 | * ---------------------------- 134 | */ 135 | 136 | .header-desktop{ 137 | background:#0da8e4; 138 | -webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.1); 139 | -moz-box-shadow:0 2px 5px 0 rgba(0,0,0,.1); 140 | box-shadow:0 2px 5px 0 rgba(0,0,0,.1); 141 | position:fixed; 142 | top:0; 143 | right:0; 144 | left:200px; 145 | height:75px; 146 | z-index:3 147 | } 148 | 149 | .header-desktop .section-content{ 150 | overflow:visible; 151 | position:absolute; 152 | top:50%; 153 | left:0; 154 | right:0; 155 | -webkit-transform:translateY(-50%); 156 | -moz-transform:translateY(-50%); 157 | -ms-transform:translateY(-50%); 158 | -o-transform:translateY(-50%); 159 | transform:translateY(-50%) 160 | } 161 | 162 | .header-wrap{ 163 | display:-webkit-box; 164 | display:-webkit-flex; 165 | display:-moz-box; 166 | display:-ms-flexbox; 167 | display:flex; 168 | -webkit-box-align:center; 169 | -webkit-align-items:center; 170 | -moz-box-align:center; 171 | -ms-flex-align:center; 172 | align-items:center; 173 | -webkit-box-pack:justify; 174 | -webkit-justify-content:space-between; 175 | -moz-box-pack:justify; 176 | -ms-flex-pack:justify; 177 | justify-content:space-between 178 | } 179 | 180 | .header-button{ 181 | display:-webkit-box; 182 | display:-webkit-flex; 183 | display:-moz-box; 184 | display:-ms-flexbox; 185 | display:flex; 186 | -webkit-box-align:center; 187 | -webkit-align-items:center; 188 | -moz-box-align:center; 189 | -ms-flex-align:center; 190 | align-items:center 191 | } 192 | 193 | .number{ 194 | padding:6px 0 195 | } 196 | 197 | .arrows i{ 198 | font-size:30px; 199 | padding:4px 4px 200 | } 201 | 202 | section{ 203 | position:relative 204 | } 205 | 206 | .section-content{ 207 | position:relative; 208 | margin:0 auto; 209 | z-index:1 210 | } 211 | 212 | .section-content-p30{ 213 | padding:0 30px 214 | } 215 | 216 | .au-input-xl{ 217 | min-width:935px 218 | } 219 | 220 | input.au-input.au-input-xl{ 221 | border-radius:40px!important; 222 | margin-right:15px 223 | } 224 | 225 | .au-input{ 226 | line-height:43px; 227 | border:1px solid #e5e5e5; 228 | font-size:14px; 229 | color:#666; 230 | padding:0 17px; 231 | -webkit-border-radius:3px; 232 | -moz-border-radius:3px; 233 | border-radius:3px; 234 | -webkit-transition:all .5s ease; 235 | -o-transition:all .5s ease; 236 | -moz-transition:all .5s ease; 237 | transition:all .5s ease 238 | } 239 | 240 | .cart-area.d-n{ 241 | display:block 242 | } 243 | 244 | .cart-area i{ 245 | font-size:22px; 246 | float:left; 247 | margin-top:16px; 248 | color:#fff; 249 | margin-left:15px 250 | } 251 | 252 | .total{ 253 | background:#205b8d; 254 | line-height:43px; 255 | color:#fff; 256 | border-radius:30px; 257 | border:1px solid #0da8e4; 258 | float:left; 259 | margin-top:7px; 260 | padding-left:17px 261 | } 262 | 263 | .total span{ 264 | display:inline-block; 265 | background:#fff; 266 | padding:0 13px; 267 | color:#000; 268 | border-top-right-radius:20px; 269 | border-bottom-right-radius:20px; 270 | margin-left:11px 271 | } 272 | 273 | .form-header{ 274 | display:-webkit-box; 275 | display:-webkit-flex; 276 | display:-moz-box; 277 | display:-ms-flexbox; 278 | display:flex 279 | } 280 | 281 | .au-btn-submit{ 282 | position:relative; 283 | right:0; 284 | min-width:95px; 285 | color:#fff; 286 | border:1px solid #205b8d; 287 | -webkit-border-radius:3px; 288 | -moz-border-radius:3px; 289 | border-radius:43px; 290 | background:#205b8d 291 | } 292 | 293 | .au-btn-submit:hover{ 294 | background:#05143f 295 | } 296 | 297 | .au-btn-submit>i{ 298 | font-size:20px; 299 | color:#fff; 300 | position:absolute; 301 | top:50%; 302 | left:50%; 303 | -webkit-transform:translate(-50%,-50%); 304 | -moz-transform:translate(-50%,-50%); 305 | -ms-transform:translate(-50%,-50%); 306 | -o-transform:translate(-50%,-50%); 307 | transform:translate(-50%,-50%) 308 | } 309 | 310 | /* 311 | * ---------------------------- 312 | * Left Menu Bar 313 | * ---------------------------- 314 | */ 315 | 316 | .menu-sidebar{ 317 | width:200px; 318 | position:fixed; 319 | left:0; 320 | top:0; 321 | bottom:0; 322 | background:#fff; 323 | overflow-y:auto 324 | } 325 | 326 | .menu-sidebar .logo{ 327 | background:#fff; 328 | height:75px; 329 | padding:0 35px; 330 | display:-webkit-box; 331 | display:-webkit-flex; 332 | display:-moz-box; 333 | display:-ms-flexbox; 334 | display:flex; 335 | -webkit-box-align:center; 336 | -webkit-align-items:center; 337 | -moz-box-align:center; 338 | -ms-flex-align:center; 339 | align-items:center; 340 | -webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.1); 341 | -moz-box-shadow:0 2px 5px 0 rgba(0,0,0,.1); 342 | box-shadow:0 2px 5px 0 rgba(0,0,0,.1); 343 | border-right:1px solid #e5e5e5; 344 | position:relative; 345 | z-index:3 346 | } 347 | 348 | .menu-sidebar .navbar-list .navbar-sub-list{ 349 | display:none; 350 | padding-left:34px 351 | } 352 | 353 | .menu-sidebar .navbar-list .navbar-sub-list li a{ 354 | padding:11.5px 0 355 | } 356 | 357 | .menu-sidebar-content{ 358 | position:relative; 359 | height:-webkit-calc(100vh - 75px); 360 | height:-moz-calc(100vh - 75px); 361 | height:calc(100vh - 75px) 362 | } 363 | 364 | .navbar-sidebar{ 365 | padding:35px; 366 | padding-bottom:0 367 | } 368 | 369 | .navbar-sidebar .navbar-list li a{ 370 | display:block; 371 | color:#555; 372 | font-size:16px; 373 | padding:5px 0 374 | } 375 | 376 | .navbar-sidebar .navbar-list li a i{ 377 | margin-right:19px 378 | } 379 | 380 | .navbar-sidebar .navbar-list li a:hover{ 381 | color:#0da8e4 382 | } 383 | 384 | /* 385 | * ---------------------------- 386 | * Header Mobile Section 387 | * ---------------------------- 388 | */ 389 | 390 | .header-mobile{ 391 | position:absolute; 392 | top:0; 393 | left:0; 394 | right:0 395 | } 396 | .header-mobile .header-mobile-bar{ 397 | padding:15px 0; background: #fff; 398 | } 399 | .header-mobile .header-mobile-inner{ 400 | display:-webkit-box; 401 | display:-webkit-flex; 402 | display:-moz-box; 403 | display:-ms-flexbox; 404 | display:flex; 405 | -webkit-box-align:center; 406 | -webkit-align-items:center; 407 | -moz-box-align:center; 408 | -ms-flex-align:center; 409 | align-items:center; 410 | -webkit-box-pack:justify; 411 | -webkit-justify-content:space-between; 412 | -moz-box-pack:justify; 413 | -ms-flex-pack:justify; 414 | justify-content:space-between 415 | } 416 | .header-mobile .hamburger{ 417 | width:36px; 418 | height:36px; 419 | padding:0; 420 | line-height:1; 421 | vertical-align:top; 422 | background:#fff; 423 | display:-webkit-box; 424 | display:-webkit-flex; 425 | display:-moz-box; 426 | display:-ms-flexbox; 427 | display:flex; 428 | -webkit-box-pack:center; 429 | -webkit-justify-content:center; 430 | -moz-box-pack:center; 431 | -ms-flex-pack:center; 432 | justify-content:center; 433 | -webkit-box-align:center; 434 | -webkit-align-items:center; 435 | -moz-box-align:center; 436 | -ms-flex-align:center; 437 | align-items:center 438 | } 439 | .header-mobile .hamburger .hamburger-box{ 440 | width:20px; 441 | height:15px 442 | } 443 | .header-mobile .hamburger .hamburger-box .hamburger-inner{ 444 | width:20px; 445 | height:2px; 446 | -webkit-border-radius:0; 447 | -moz-border-radius:0; 448 | border-radius:0 449 | } 450 | .header-mobile .hamburger .hamburger-box .hamburger-inner:before{ 451 | width:20px; 452 | height:2px; 453 | top:6px 454 | } 455 | .header-mobile .hamburger .hamburger-box .hamburger-inner:after{ 456 | top:12px; 457 | width:20px; 458 | height:2px 459 | } 460 | .header-mobile .navbar-mobile{ 461 | display:none; 462 | position:absolute; 463 | width:100%; 464 | top:88px; 465 | z-index:20 466 | } 467 | .header-mobile .navbar-mobile .navbar-mobile-list{ 468 | background:#f8f8f8 469 | } 470 | .header-mobile .navbar-mobile .navbar-mobile-list>li>a{ 471 | padding-left:15px!important 472 | } 473 | .header-mobile .navbar-mobile .navbar-mobile-list li a{ 474 | color:#555; 475 | display:block; 476 | padding:10px 15px; 477 | padding-right:25px; 478 | padding-left:0; 479 | border-bottom:1px solid #e6e6e6; 480 | text-transform:capitalize; 481 | line-height:inherit 482 | } 483 | .header-mobile .navbar-mobile .navbar-mobile-list li a:hover{ 484 | color:#0da8e4 485 | } 486 | .header-mobile .navbar-mobile .navbar-mobile-list li a>i{ 487 | margin-right:19px 488 | } 489 | .navbar-mobile-sub-list{ 490 | display:none; 491 | padding-left:30px; 492 | background:#fff 493 | } 494 | .header-mobile .navbar-mobile .navbar-mobile-sub-list li a{ 495 | padding-left:15px 496 | } 497 | .header-mobile-2{ 498 | background:#393939; 499 | position:static 500 | } 501 | .header-mobile-2.header-mobile .navbar-mobile{ 502 | top:82px 503 | } 504 | .header-mobile-2.header-mobile .hamburger{ 505 | background:0 0 506 | } 507 | .header-mobile-2.header-mobile .hamburger .hamburger-box .hamburger-inner{ 508 | background:#fff 509 | } 510 | .header-mobile-2.header-mobile .hamburger .hamburger-box .hamburger-inner::before{ 511 | background:#fff 512 | } 513 | .header-mobile-2.header-mobile .hamburger .hamburger-box .hamburger-inner::after{ 514 | background:#fff 515 | } 516 | .main-content.page-m{ 517 | padding: 10% 30% 10% 5% 518 | } 519 | 520 | /* 521 | * ---------------------------- 522 | * Products List 523 | * ---------------------------- 524 | */ 525 | 526 | .product-box img { 527 | width: 150px; 528 | } 529 | 530 | .items select { 531 | width: 100px; 532 | height: 40px; 533 | border-radius: 40px; 534 | padding-left: 15px; 535 | margin-bottom: 10px; 536 | outline: 0; 537 | margin-left: 5px; 538 | } 539 | .product-box{ 540 | margin-bottom:60px 541 | } 542 | .price{ 543 | margin:15px 0 0; 544 | font-weight:800; 545 | color:#0da8e4 546 | } 547 | .product-box h1{ 548 | font-size:18px; 549 | border-bottom:1px solid #ccc; 550 | padding-bottom:5px; 551 | font-weight:600; 552 | padding-top:12px; 553 | margin-bottom:7px 554 | } 555 | .product-box h2{ 556 | font-size:15px; 557 | padding-bottom:0; 558 | font-weight:500; 559 | color:#585858 560 | } 561 | 562 | /* 563 | * ---------------------------- 564 | * Shopping Cart Checkout 565 | * ---------------------------- 566 | */ 567 | 568 | .form-area{ 569 | background:#e5e5e5; 570 | position:relative; 571 | margin-bottom:45px; 572 | padding:50px 20px; 573 | border:3px solid #ccc 574 | } 575 | .form-area h3{ 576 | position:absolute; 577 | top:-26px; 578 | left:13px; 579 | background:#e5e5e5; 580 | padding:10px 20px; 581 | font-weight:500; 582 | z-index:23; 583 | display:inline-block 584 | } 585 | .input-space{ 586 | margin-bottom:20px 587 | } 588 | .input-space label{ 589 | padding-left:31px; 590 | font-size:14px; 591 | margin-bottom:50px 592 | } 593 | .input-space input[type=text]{ 594 | width:100%; 595 | height:40px; 596 | border-radius:30px; 597 | outline:0; 598 | padding:10px; 599 | border:1px solid #ccc 600 | } 601 | .input-space select{ 602 | width:100%; 603 | height:40px; 604 | border-radius:30px; 605 | outline:0; 606 | padding:0 10px; 607 | border:1px solid #ccc 608 | } 609 | .au-checkbox{ 610 | display:block; 611 | position:relative; 612 | cursor:pointer; 613 | font-size:22px; 614 | -webkit-user-select:none; 615 | -moz-user-select:none; 616 | -ms-user-select:none; 617 | user-select:none 618 | } 619 | .au-checkbox input{ 620 | position:absolute; 621 | opacity:0; 622 | cursor:pointer 623 | } 624 | .au-checkmark{ 625 | position:absolute; 626 | top:0; 627 | left:0; 628 | height:22px; 629 | width:22px; 630 | background-color:#fff!important; 631 | border:2px solid #e5e5e5; 632 | -webkit-border-radius:2px; 633 | -moz-border-radius:2px; 634 | border-radius:2px 635 | } 636 | .au-checkbox:hover input~.au-checkmark{ 637 | background-color:transparent 638 | } 639 | .au-checkbox input:checked~.au-checkmark{ 640 | background-color:#fff 641 | } 642 | .au-checkmark:after{ 643 | content:""; 644 | position:absolute; 645 | display:none 646 | } 647 | .au-checkbox input:checked~.au-checkmark:after{ 648 | display:block 649 | } 650 | label.date{ 651 | padding-top:8px 652 | } 653 | .au-checkbox .au-checkmark:after{ 654 | left:5px; 655 | top:-1px; 656 | width:9px; 657 | height:15px; 658 | border:solid #0da8e4; 659 | border-width:0 4px 4px 0; 660 | -webkit-border-radius:2px; 661 | -moz-border-radius:2px; 662 | border-radius:2px; 663 | -webkit-transform:rotate(45deg); 664 | -moz-transform:rotate(45deg); 665 | -ms-transform:rotate(45deg); 666 | -o-transform:rotate(45deg); 667 | transform:rotate(45deg) 668 | } 669 | option:first{ 670 | color:#ccc 671 | } 672 | select{ 673 | color:#828482 674 | } 675 | 676 | /* 677 | * ---------------------------- 678 | * Shopping Cart Details 679 | * ---------------------------- 680 | */ 681 | 682 | .table{ 683 | margin:0 684 | } 685 | table.table.table-bordered{ 686 | border:1px solid #ccc; 687 | background:#fff 688 | } 689 | .table-bordered td,.table-bordered th{ 690 | border:1px solid #dee2e6 691 | } 692 | table.table.table-bordered th{ 693 | BACKGROUND:#0da8e4; 694 | color:#fff; 695 | font-size:14px; 696 | padding:7px 697 | } 698 | .table-responsive{ 699 | padding-right:1px 700 | } 701 | .items select{ 702 | width:100px; 703 | height:40px; 704 | border-radius:40px; 705 | padding-left:15px; 706 | margin-bottom:10px; 707 | outline:0; 708 | margin-left:5px 709 | } 710 | 711 | /* 712 | * ---------------------------- 713 | * Product Details 714 | * ---------------------------- 715 | */ 716 | 717 | .price{ 718 | margin:15px 0 0; 719 | font-weight:800; 720 | color:#0da8e4 721 | } 722 | .detail-section{ 723 | margin-top:60px 724 | } 725 | 726 | .detail-img{ 727 | width: 30%; 728 | } 729 | 730 | /**/ 731 | 732 | /* 733 | * ---------------------------- 734 | * Buttons 735 | * ---------------------------- 736 | */ 737 | 738 | .btn-info{ 739 | color:#fff; 740 | background-color:#0da8e4!important; 741 | border-color:#0da8e4!important 742 | } 743 | a.primary-btn{ 744 | background:#05143f; 745 | padding:7px 20px; 746 | text-align:center; 747 | display:inline-block; 748 | color:#fff; 749 | border-radius:2px; 750 | font-size:14px; 751 | margin-top:5px 752 | } 753 | a.primary-btn:hover{ 754 | background:#0da8e4; 755 | color:#fff 756 | } 757 | 758 | .active-link { 759 | font-weight: bold; 760 | } 761 | 762 | /* 763 | * ---------------------------- 764 | * Footer section 765 | * ---------------------------- 766 | */ 767 | 768 | footer ul li a{ 769 | color:#fff 770 | } 771 | footer{ 772 | background:#232324; 773 | z-index:1; 774 | position:relative; 775 | text-align:center 776 | } 777 | footer li a{ 778 | color:#313131; 779 | font-weight:500 780 | } 781 | footer ul li{ 782 | display:inline-block; 783 | font-size:14px; 784 | padding:10px 20px 785 | } 786 | 787 | /* 788 | * ---------------------------- 789 | * Media Queries 790 | * ---------------------------- 791 | */ 792 | 793 | @media (max-width:1600px){ 794 | .au-input-xl{ 795 | min-width:290px 796 | } 797 | } 798 | @media (max-width:1315px) and (min-width:992px){ 799 | .header-desktop3 .section-content{ 800 | padding:0 15px 801 | } 802 | } 803 | @media (max-width:1570px) and (min-width:992px){ 804 | .header-navbar ul li a{ 805 | padding:25px 15px 806 | } 807 | } 808 | @media (max-width:1315px) and (min-width:992px){ 809 | .header-navbar ul li a{ 810 | font-size:13px; 811 | padding:27px 15px 812 | } 813 | .header-navbar ul li a i{ 814 | margin-right:5px 815 | } 816 | } 817 | 818 | @media (min-width:1200px){ 819 | .container{ 820 | max-width:1320px 821 | } 822 | } 823 | 824 | @media (min-width:992px) and (max-width:1199px){ 825 | .cart-area i{ 826 | font-size:22px; 827 | float:left; 828 | margin-top:16px; 829 | color:#205b8d; 830 | margin-left:15px 831 | } 832 | .header-mobile .header-mobile-bar{ 833 | padding:10px 0; 834 | background:#fff 835 | } 836 | .header-wrap .account-item>.content{ 837 | display:none 838 | } 839 | } 840 | 841 | 842 | @media (max-width:991px){ 843 | .section-content-p30{ 844 | padding:0 845 | } 846 | .page-container{ 847 | position:relative; 848 | top:88px; 849 | padding-left:0 850 | } 851 | .page-wrapper{ 852 | background:#e5e5e5; 853 | padding-bottom:12vh 854 | } 855 | .main-content{ 856 | padding-top:50px; 857 | padding-bottom:100px 858 | } 859 | .cart-area.d-n{ 860 | display:none 861 | } 862 | .form-header{ 863 | -webkit-box-pack:center; 864 | -webkit-justify-content:center; 865 | -moz-box-pack:center; 866 | -ms-flex-pack:center; 867 | justify-content:center 868 | } 869 | .au-input-xl{ 870 | min-width:350px 871 | } 872 | .logo{ 873 | text-align:center 874 | } 875 | .header-desktop{ 876 | position:relative; 877 | top:0; 878 | left:0; 879 | height:80px 880 | } 881 | .header-wrap{ 882 | -webkit-box-orient:vertical; 883 | -webkit-box-direction:normal; 884 | -webkit-flex-direction:column; 885 | -moz-box-orient:vertical; 886 | -moz-box-direction:normal; 887 | -ms-flex-direction:column; 888 | flex-direction:column 889 | } 890 | .header-button{ 891 | margin-top:30px; 892 | width:100%; 893 | -webkit-box-pack:justify; 894 | -webkit-justify-content:space-between; 895 | -moz-box-pack:justify; 896 | -ms-flex-pack:justify; 897 | justify-content:space-between 898 | } 899 | .menu-sidebar2{ 900 | top:0; 901 | right:-300px; 902 | left:auto; 903 | -webkit-box-shadow:0 5px 10px 0 rgba(0,0,0,.1); 904 | -moz-box-shadow:0 5px 10px 0 rgba(0,0,0,.1); 905 | box-shadow:0 5px 10px 0 rgba(0,0,0,.1) 906 | } 907 | .header-desktop2{ 908 | left:0; 909 | position:relative 910 | } 911 | .header-desktop4{ 912 | position:static 913 | } 914 | .section-content.section-content-p30.e-spc{ 915 | padding-top:100px 916 | } 917 | header.header-desktop.extra{ 918 | top:93px!important 919 | } 920 | .cart-area i{ 921 | font-size:22px; 922 | float:left; 923 | margin-top:16px; 924 | color:#205b8d; 925 | margin-left:15px 926 | } 927 | .header-wrap2{ 928 | -webkit-box-pack:justify; 929 | -webkit-justify-content:space-between; 930 | -moz-box-pack:justify; 931 | -ms-flex-pack:justify; 932 | justify-content:space-between 933 | } 934 | } 935 | 936 | 937 | @media (max-width:767px){ 938 | .au-input-xl{ 939 | min-width:150px 940 | } 941 | .header-button-item{ 942 | font-size:22px; 943 | margin-right:15px 944 | } 945 | .main-content.page-m{ 946 | padding:10% 0!important 947 | } 948 | .header-mobile .navbar-mobile{ 949 | display:none; 950 | position:absolute; 951 | width:100%; 952 | top:66px; 953 | z-index:20 954 | } 955 | .section-content.section-content-p30.e-spc{ 956 | padding-top:100px 957 | } 958 | header.header-desktop.extra{ 959 | top:64px!important 960 | } 961 | .header-desktop{ 962 | position:relative; 963 | top:0; 964 | left:0; 965 | height:80px 966 | } 967 | a.logo img{ 968 | width:110px 969 | } 970 | 971 | .cart-area i{ 972 | font-size:22px; 973 | float:left; 974 | margin-top:16px; 975 | color:#205b8d; 976 | margin-left:15px 977 | } 978 | .footer-pagination{ 979 | text-align:center 980 | } 981 | .product-box{ 982 | text-align:centerg 983 | } 984 | .page-container{ 985 | position:relative; 986 | top:66px; 987 | padding-left:0 988 | } 989 | .au-btn-submit{ 990 | position:relative; 991 | right:10px; 992 | min-width:85px; 993 | color:#fff; 994 | border:1px solid #205b8d; 995 | -webkit-border-radius:3px; 996 | -moz-border-radius:3px; 997 | border-radius:0; 998 | background:#205b8d 999 | } 1000 | input.au-input.au-input-xl{ 1001 | border-radius:0!important; 1002 | margin-right:0 1003 | } 1004 | .cart-area.d-b{ 1005 | display:block 1006 | } 1007 | .cart-area.d-n{ 1008 | display:none 1009 | } 1010 | .cart-area i{ 1011 | font-size:18px; 1012 | float:left; 1013 | margin-top:16px; 1014 | color:#03a9f4; 1015 | margin-left:15px 1016 | } 1017 | .total span{ 1018 | display:inline-block; 1019 | background:#fff; 1020 | padding:0 10px; 1021 | color:#000; 1022 | border-top-right-radius:20px; 1023 | border-bottom-right-radius:20px; 1024 | margin-left:6px 1025 | } 1026 | .total{ 1027 | background:#205b8d; 1028 | line-height:30px; 1029 | color:#fff; 1030 | border-radius:30px; 1031 | border:1px solid #205b8d; 1032 | float:left; 1033 | margin-top:9px; 1034 | padding-left:12px 1035 | } 1036 | .header-mobile .header-mobile-bar{ 1037 | padding:6px 0; 1038 | background:#fff; 1039 | border:0; 1040 | padding:8px 0 1041 | } 1042 | } -------------------------------------------------------------------------------- /frontend/angular-ecommerce/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "lib": [ 15 | "es2018", 16 | "dom" 17 | ] 18 | }, 19 | "angularCompilerOptions": { 20 | "fullTemplateTypeCheck": true, 21 | "strictInjectionParameters": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /frontend/angular-ecommerce/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-return-shorthand": true, 12 | "curly": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "directive-class-suffix": true, 19 | "directive-selector": [ 20 | true, 21 | "attribute", 22 | "app", 23 | "camelCase" 24 | ], 25 | "component-selector": [ 26 | true, 27 | "element", 28 | "app", 29 | "kebab-case" 30 | ], 31 | "eofline": true, 32 | "import-blacklist": [ 33 | true, 34 | "rxjs/Rx" 35 | ], 36 | "import-spacing": true, 37 | "indent": { 38 | "options": [ 39 | "spaces" 40 | ] 41 | }, 42 | "max-classes-per-file": false, 43 | "max-line-length": [ 44 | true, 45 | 140 46 | ], 47 | "member-ordering": [ 48 | true, 49 | { 50 | "order": [ 51 | "static-field", 52 | "instance-field", 53 | "static-method", 54 | "instance-method" 55 | ] 56 | } 57 | ], 58 | "no-console": [ 59 | true, 60 | "debug", 61 | "info", 62 | "time", 63 | "timeEnd", 64 | "trace" 65 | ], 66 | "no-empty": false, 67 | "no-inferrable-types": [ 68 | true, 69 | "ignore-params" 70 | ], 71 | "no-non-null-assertion": true, 72 | "no-redundant-jsdoc": true, 73 | "no-switch-case-fall-through": true, 74 | "no-var-requires": false, 75 | "object-literal-key-quotes": [ 76 | true, 77 | "as-needed" 78 | ], 79 | "quotemark": [ 80 | true, 81 | "single" 82 | ], 83 | "semicolon": { 84 | "options": [ 85 | "always" 86 | ] 87 | }, 88 | "space-before-function-paren": { 89 | "options": { 90 | "anonymous": "never", 91 | "asyncArrow": "always", 92 | "constructor": "never", 93 | "method": "never", 94 | "named": "never" 95 | } 96 | }, 97 | "typedef-whitespace": { 98 | "options": [ 99 | { 100 | "call-signature": "nospace", 101 | "index-signature": "nospace", 102 | "parameter": "nospace", 103 | "property-declaration": "nospace", 104 | "variable-declaration": "nospace" 105 | }, 106 | { 107 | "call-signature": "onespace", 108 | "index-signature": "onespace", 109 | "parameter": "onespace", 110 | "property-declaration": "onespace", 111 | "variable-declaration": "onespace" 112 | } 113 | ] 114 | }, 115 | "variable-name": { 116 | "options": [ 117 | "ban-keywords", 118 | "check-format", 119 | "allow-pascal-case" 120 | ] 121 | }, 122 | "whitespace": { 123 | "options": [ 124 | "check-branch", 125 | "check-decl", 126 | "check-operator", 127 | "check-separator", 128 | "check-type", 129 | "check-typecast" 130 | ] 131 | }, 132 | "no-conflicting-lifecycle": true, 133 | "no-host-metadata-property": true, 134 | "no-input-rename": true, 135 | "no-inputs-metadata-property": true, 136 | "no-output-native": true, 137 | "no-output-on-prefix": true, 138 | "no-output-rename": true, 139 | "no-outputs-metadata-property": true, 140 | "template-banana-in-box": true, 141 | "template-no-negated-async": true, 142 | "use-lifecycle-interface": true, 143 | "use-pipe-transform-interface": true 144 | }, 145 | "rulesDirectory": [ 146 | "codelyzer" 147 | ] 148 | } --------------------------------------------------------------------------------