├── README.md
├── application.py
├── data.db
├── static
├── css
│ └── custom.css
├── img
│ ├── argentina.png
│ ├── belgium.png
│ ├── bj.jpg
│ ├── brasil.jpg
│ ├── colombia.jpg
│ ├── cor.png
│ ├── costarica.jpg
│ ├── croacia.jpg
│ ├── egypt.jpg
│ ├── england.jpg
│ ├── fcb.jpg
│ ├── fla.png
│ ├── france.jpg
│ ├── germany.png
│ ├── iceland.jpg
│ ├── juv.jpg
│ ├── mc.jpg
│ ├── mexico.jpg
│ ├── mil.png
│ ├── mu.png
│ ├── peru.jpg
│ ├── rm.png
│ ├── rp.png
│ ├── sale-icon.png
│ ├── senegal.jpg
│ ├── spain.jpg
│ ├── sweden.jpg
│ └── uruguay.png
└── js
│ ├── myscripts.js
│ └── validate.js
└── templates
├── base.html
├── cart.html
├── history.html
├── index.html
├── login.html
└── new.html
/README.md:
--------------------------------------------------------------------------------
1 | # flask-ecomm
2 | An eCommerce App built with Flask, Jinja, SQLite, jQuery and Bootstrap
3 |
4 | This app was my CS50x final project.
5 |
6 | The app loads a gallery of soccer shirts that includes: image, description, price, and a small form to add item to cart. The shirt info is stored in a SQLite database and is displayed using Bootstrap's card class.
7 |
8 |
9 |
10 | The app includes a series of filters implemented using SQLite queries so you can see only shirts that match a certain filter: shirts by region, clubs vs. national teams, shirts on sale, etc.
11 |
12 |
13 |
14 | If a user is not logged in and tries to add something to the shopping cart she will see a warning message (implemented with jQuery) asking her to log in.
15 |
16 | Once registered and logged in, the user can add shirts to the shopping cart. A link to the shopping cart can be found at the top right of the screen, showing the amount of items in the cart as well as the sub-total in dollars. Clicking the shopping cart link opens a Bootstrap modal window showing the shopping cart in more detail.
17 |
18 |
19 |
20 | If you want to make changes, like add one more shirt or remove a shirt, you can click on the Make Changes button and you will be taken to the full version of the shopping cart.
21 |
22 |
23 |
24 | Once you check out, the idea is to be sent to a payment processor. However, that part is not implemented yet.
25 |
26 | If you want to see your purchase history, just click on the You Bought link and you will see all the shirts you have ever bought. You will also find a Buy Again link that will direct you to the product page in case you want to buy it again.
27 |
28 |
29 |
30 | Once you're finished, you can just log out.
31 |
32 | If you want to see the app in action, fork the repository to your own computer and perform the following commands from the command line in your project folder:
33 |
34 |
export FLASK_APP=application.py
35 | flask run
36 |
37 | This assumes you have Python, Flask and SQLite installed in your computer, as well as a link to Bootstrap and the following modules necessary to run application.py installed:
38 |
39 |
40 | from cs50 import SQL
41 | from flask_session import Session
42 | from flask import Flask, render_template, redirect, request, session, jsonify
43 | from datetime import datetime
44 |
45 |
--------------------------------------------------------------------------------
/application.py:
--------------------------------------------------------------------------------
1 | from cs50 import SQL
2 | from flask_session import Session
3 | from flask import Flask, render_template, redirect, request, session, jsonify
4 | from datetime import datetime
5 |
6 | # # Instantiate Flask object named app
7 | app = Flask(__name__)
8 |
9 | # # Configure sessions
10 | app.config["SESSION_PERMANENT"] = False
11 | app.config["SESSION_TYPE"] = "filesystem"
12 | Session(app)
13 |
14 | # Creates a connection to the database
15 | db = SQL ( "sqlite:///data.db" )
16 |
17 | @app.route("/")
18 | def index():
19 | shirts = db.execute("SELECT * FROM shirts ORDER BY team ASC")
20 | shirtsLen = len(shirts)
21 | # Initialize variables
22 | shoppingCart = []
23 | shopLen = len(shoppingCart)
24 | totItems, total, display = 0, 0, 0
25 | if 'user' in session:
26 | shoppingCart = db.execute("SELECT team, image, SUM(qty), SUM(subTotal), price, id FROM cart GROUP BY team")
27 | shopLen = len(shoppingCart)
28 | for i in range(shopLen):
29 | total += shoppingCart[i]["SUM(subTotal)"]
30 | totItems += shoppingCart[i]["SUM(qty)"]
31 | shirts = db.execute("SELECT * FROM shirts ORDER BY team ASC")
32 | shirtsLen = len(shirts)
33 | return render_template ("index.html", shoppingCart=shoppingCart, shirts=shirts, shopLen=shopLen, shirtsLen=shirtsLen, total=total, totItems=totItems, display=display, session=session )
34 | return render_template ( "index.html", shirts=shirts, shoppingCart=shoppingCart, shirtsLen=shirtsLen, shopLen=shopLen, total=total, totItems=totItems, display=display)
35 |
36 |
37 | @app.route("/buy/")
38 | def buy():
39 | # Initialize shopping cart variables
40 | shoppingCart = []
41 | shopLen = len(shoppingCart)
42 | totItems, total, display = 0, 0, 0
43 | qty = int(request.args.get('quantity'))
44 | if session:
45 | # Store id of the selected shirt
46 | id = int(request.args.get('id'))
47 | # Select info of selected shirt from database
48 | goods = db.execute("SELECT * FROM shirts WHERE id = :id", id=id)
49 | # Extract values from selected shirt record
50 | # Check if shirt is on sale to determine price
51 | if(goods[0]["onSale"] == 1):
52 | price = goods[0]["onSalePrice"]
53 | else:
54 | price = goods[0]["price"]
55 | team = goods[0]["team"]
56 | image = goods[0]["image"]
57 | subTotal = qty * price
58 | # Insert selected shirt into shopping cart
59 | db.execute("INSERT INTO cart (id, qty, team, image, price, subTotal) VALUES (:id, :qty, :team, :image, :price, :subTotal)", id=id, qty=qty, team=team, image=image, price=price, subTotal=subTotal)
60 | shoppingCart = db.execute("SELECT team, image, SUM(qty), SUM(subTotal), price, id FROM cart GROUP BY team")
61 | shopLen = len(shoppingCart)
62 | # Rebuild shopping cart
63 | for i in range(shopLen):
64 | total += shoppingCart[i]["SUM(subTotal)"]
65 | totItems += shoppingCart[i]["SUM(qty)"]
66 | # Select all shirts for home page view
67 | shirts = db.execute("SELECT * FROM shirts ORDER BY team ASC")
68 | shirtsLen = len(shirts)
69 | # Go back to home page
70 | return render_template ("index.html", shoppingCart=shoppingCart, shirts=shirts, shopLen=shopLen, shirtsLen=shirtsLen, total=total, totItems=totItems, display=display, session=session )
71 |
72 |
73 | @app.route("/update/")
74 | def update():
75 | # Initialize shopping cart variables
76 | shoppingCart = []
77 | shopLen = len(shoppingCart)
78 | totItems, total, display = 0, 0, 0
79 | qty = int(request.args.get('quantity'))
80 | if session:
81 | # Store id of the selected shirt
82 | id = int(request.args.get('id'))
83 | db.execute("DELETE FROM cart WHERE id = :id", id=id)
84 | # Select info of selected shirt from database
85 | goods = db.execute("SELECT * FROM shirts WHERE id = :id", id=id)
86 | # Extract values from selected shirt record
87 | # Check if shirt is on sale to determine price
88 | if(goods[0]["onSale"] == 1):
89 | price = goods[0]["onSalePrice"]
90 | else:
91 | price = goods[0]["price"]
92 | team = goods[0]["team"]
93 | image = goods[0]["image"]
94 | subTotal = qty * price
95 | # Insert selected shirt into shopping cart
96 | db.execute("INSERT INTO cart (id, qty, team, image, price, subTotal) VALUES (:id, :qty, :team, :image, :price, :subTotal)", id=id, qty=qty, team=team, image=image, price=price, subTotal=subTotal)
97 | shoppingCart = db.execute("SELECT team, image, SUM(qty), SUM(subTotal), price, id FROM cart GROUP BY team")
98 | shopLen = len(shoppingCart)
99 | # Rebuild shopping cart
100 | for i in range(shopLen):
101 | total += shoppingCart[i]["SUM(subTotal)"]
102 | totItems += shoppingCart[i]["SUM(qty)"]
103 | # Go back to cart page
104 | return render_template ("cart.html", shoppingCart=shoppingCart, shopLen=shopLen, total=total, totItems=totItems, display=display, session=session )
105 |
106 |
107 | @app.route("/filter/")
108 | def filter():
109 | if request.args.get('continent'):
110 | query = request.args.get('continent')
111 | shirts = db.execute("SELECT * FROM shirts WHERE continent = :query ORDER BY team ASC", query=query )
112 | if request.args.get('sale'):
113 | query = request.args.get('sale')
114 | shirts = db.execute("SELECT * FROM shirts WHERE onSale = :query ORDER BY team ASC", query=query)
115 | if request.args.get('id'):
116 | query = int(request.args.get('id'))
117 | shirts = db.execute("SELECT * FROM shirts WHERE id = :query ORDER BY team ASC", query=query)
118 | if request.args.get('kind'):
119 | query = request.args.get('kind')
120 | shirts = db.execute("SELECT * FROM shirts WHERE kind = :query ORDER BY team ASC", query=query)
121 | if request.args.get('price'):
122 | query = request.args.get('price')
123 | shirts = db.execute("SELECT * FROM shirts ORDER BY onSalePrice ASC")
124 | shirtsLen = len(shirts)
125 | # Initialize shopping cart variables
126 | shoppingCart = []
127 | shopLen = len(shoppingCart)
128 | totItems, total, display = 0, 0, 0
129 | if 'user' in session:
130 | # Rebuild shopping cart
131 | shoppingCart = db.execute("SELECT team, image, SUM(qty), SUM(subTotal), price, id FROM cart GROUP BY team")
132 | shopLen = len(shoppingCart)
133 | for i in range(shopLen):
134 | total += shoppingCart[i]["SUM(subTotal)"]
135 | totItems += shoppingCart[i]["SUM(qty)"]
136 | # Render filtered view
137 | return render_template ("index.html", shoppingCart=shoppingCart, shirts=shirts, shopLen=shopLen, shirtsLen=shirtsLen, total=total, totItems=totItems, display=display, session=session )
138 | # Render filtered view
139 | return render_template ( "index.html", shirts=shirts, shoppingCart=shoppingCart, shirtsLen=shirtsLen, shopLen=shopLen, total=total, totItems=totItems, display=display)
140 |
141 |
142 | @app.route("/checkout/")
143 | def checkout():
144 | order = db.execute("SELECT * from cart")
145 | # Update purchase history of current customer
146 | for item in order:
147 | db.execute("INSERT INTO purchases (uid, id, team, image, quantity) VALUES(:uid, :id, :team, :image, :quantity)", uid=session["uid"], id=item["id"], team=item["team"], image=item["image"], quantity=item["qty"] )
148 | # Clear shopping cart
149 | db.execute("DELETE from cart")
150 | shoppingCart = []
151 | shopLen = len(shoppingCart)
152 | totItems, total, display = 0, 0, 0
153 | # Redirect to home page
154 | return redirect('/')
155 |
156 |
157 | @app.route("/remove/", methods=["GET"])
158 | def remove():
159 | # Get the id of shirt selected to be removed
160 | out = int(request.args.get("id"))
161 | # Remove shirt from shopping cart
162 | db.execute("DELETE from cart WHERE id=:id", id=out)
163 | # Initialize shopping cart variables
164 | totItems, total, display = 0, 0, 0
165 | # Rebuild shopping cart
166 | shoppingCart = db.execute("SELECT team, image, SUM(qty), SUM(subTotal), price, id FROM cart GROUP BY team")
167 | shopLen = len(shoppingCart)
168 | for i in range(shopLen):
169 | total += shoppingCart[i]["SUM(subTotal)"]
170 | totItems += shoppingCart[i]["SUM(qty)"]
171 | # Turn on "remove success" flag
172 | display = 1
173 | # Render shopping cart
174 | return render_template ("cart.html", shoppingCart=shoppingCart, shopLen=shopLen, total=total, totItems=totItems, display=display, session=session )
175 |
176 |
177 | @app.route("/login/", methods=["GET"])
178 | def login():
179 | return render_template("login.html")
180 |
181 |
182 | @app.route("/new/", methods=["GET"])
183 | def new():
184 | # Render log in page
185 | return render_template("new.html")
186 |
187 |
188 | @app.route("/logged/", methods=["POST"] )
189 | def logged():
190 | # Get log in info from log in form
191 | user = request.form["username"].lower()
192 | pwd = request.form["password"]
193 | #pwd = str(sha1(request.form["password"].encode('utf-8')).hexdigest())
194 | # Make sure form input is not blank and re-render log in page if blank
195 | if user == "" or pwd == "":
196 | return render_template ( "login.html" )
197 | # Find out if info in form matches a record in user database
198 | query = "SELECT * FROM users WHERE username = :user AND password = :pwd"
199 | rows = db.execute ( query, user=user, pwd=pwd )
200 |
201 | # If username and password match a record in database, set session variables
202 | if len(rows) == 1:
203 | session['user'] = user
204 | session['time'] = datetime.now( )
205 | session['uid'] = rows[0]["id"]
206 | # Redirect to Home Page
207 | if 'user' in session:
208 | return redirect ( "/" )
209 | # If username is not in the database return the log in page
210 | return render_template ( "login.html", msg="Wrong username or password." )
211 |
212 |
213 | @app.route("/history/")
214 | def history():
215 | # Initialize shopping cart variables
216 | shoppingCart = []
217 | shopLen = len(shoppingCart)
218 | totItems, total, display = 0, 0, 0
219 | # Retrieve all shirts ever bought by current user
220 | myShirts = db.execute("SELECT * FROM purchases WHERE uid=:uid", uid=session["uid"])
221 | myShirtsLen = len(myShirts)
222 | # Render table with shopping history of current user
223 | return render_template("history.html", shoppingCart=shoppingCart, shopLen=shopLen, total=total, totItems=totItems, display=display, session=session, myShirts=myShirts, myShirtsLen=myShirtsLen)
224 |
225 |
226 | @app.route("/logout/")
227 | def logout():
228 | # clear shopping cart
229 | db.execute("DELETE from cart")
230 | # Forget any user_id
231 | session.clear()
232 | # Redirect user to login form
233 | return redirect("/")
234 |
235 |
236 | @app.route("/register/", methods=["POST"] )
237 | def registration():
238 | # Get info from form
239 | username = request.form["username"]
240 | password = request.form["password"]
241 | confirm = request.form["confirm"]
242 | fname = request.form["fname"]
243 | lname = request.form["lname"]
244 | email = request.form["email"]
245 | # See if username already in the database
246 | rows = db.execute( "SELECT * FROM users WHERE username = :username ", username = username )
247 | # If username already exists, alert user
248 | if len( rows ) > 0:
249 | return render_template ( "new.html", msg="Username already exists!" )
250 | # If new user, upload his/her info into the users database
251 | new = db.execute ( "INSERT INTO users (username, password, fname, lname, email) VALUES (:username, :password, :fname, :lname, :email)",
252 | username=username, password=password, fname=fname, lname=lname, email=email )
253 | # Render login template
254 | return render_template ( "login.html" )
255 |
256 |
257 | @app.route("/cart/")
258 | def cart():
259 | if 'user' in session:
260 | # Clear shopping cart variables
261 | totItems, total, display = 0, 0, 0
262 | # Grab info currently in database
263 | shoppingCart = db.execute("SELECT team, image, SUM(qty), SUM(subTotal), price, id FROM cart GROUP BY team")
264 | # Get variable values
265 | shopLen = len(shoppingCart)
266 | for i in range(shopLen):
267 | total += shoppingCart[i]["SUM(subTotal)"]
268 | totItems += shoppingCart[i]["SUM(qty)"]
269 | # Render shopping cart
270 | return render_template("cart.html", shoppingCart=shoppingCart, shopLen=shopLen, total=total, totItems=totItems, display=display, session=session)
271 |
272 |
273 | # @app.errorhandler(404)
274 | # def pageNotFound( e ):
275 | # if 'user' in session:
276 | # return render_template ( "404.html", session=session )
277 | # return render_template ( "404.html" ), 404
278 |
279 |
280 | # Only needed if Flask run is not used to execute the server
281 | #if __name__ == "__main__":
282 | # app.run( host='0.0.0.0', port=8080 )
283 |
--------------------------------------------------------------------------------
/data.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/data.db
--------------------------------------------------------------------------------
/static/css/custom.css:
--------------------------------------------------------------------------------
1 | .card:hover {
2 | border-color: #999;
3 | box-shadow: 1px 2px #999;
4 | }
5 |
6 | .card {
7 | margin-bottom: 1em;
8 | }
9 |
10 | .price {
11 | color: seagreen;
12 | font-weight: bold;
13 | }
14 |
15 | .price:before {
16 | content: '$';
17 | }
18 |
19 | .shirt {
20 | margin-bottom: 10px;
21 | width: 200px;
22 | }
23 |
24 | .stepper-input{
25 | display: flex;
26 | display: -webkit-flex;
27 | color: #222;
28 | max-width: 120px;
29 | margin: 10px auto;
30 | text-align: center;
31 | }
32 |
33 | header {
34 | margin-bottom: 50px;
35 | }
36 |
37 | .shirtCart {
38 | width: 25px;
39 | }
40 |
41 | .add {
42 | text-transform: uppercase;
43 | font-size: 0.8em;
44 | font-weight: bold;
45 | color: white;
46 | }
47 |
48 | .checkout {
49 | text-transform: uppercase;
50 | font-size: 0.8em;
51 | font-weight: bold;
52 | }
53 |
54 | .add:hover {
55 | background-color: deepskyblue;
56 | border-color: deepskyblue;
57 | }
58 |
59 | tr {
60 | text-align: center;
61 | }
62 |
63 | .modal-header {
64 | border-bottom: 0px;
65 | }
66 |
67 | .counter {
68 | font-size: 0.6em;
69 | margin-left: 1em;
70 | font-weight: bold;
71 | }
72 |
73 |
74 | .increment,
75 | .decrement{
76 | height: 24px;
77 | width: 24px;
78 | border: 1px solid #222;
79 | text-align: center;
80 | box-sizing: border-box;
81 | border-radius: 50%;
82 | text-decoration: none;
83 | color: #222;
84 | font-size: 24px;
85 | line-height: 22px;
86 | display: inline-block;
87 | cursor: pointer;
88 | }
89 |
90 | .decrement:hover,
91 | .increment:hover {
92 | color: green;
93 | }
94 |
95 | .decrement:active,
96 | .increment:active {
97 | background-color: green;
98 | color: white;
99 | }
100 |
101 |
102 | .quantity{
103 | height: 24px;
104 | width: 48px;
105 | text-align: center;
106 | margin: 0 12px;
107 | border-radius: 2px;
108 | border: 1px solid #222;
109 |
110 | }
--------------------------------------------------------------------------------
/static/img/argentina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/argentina.png
--------------------------------------------------------------------------------
/static/img/belgium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/belgium.png
--------------------------------------------------------------------------------
/static/img/bj.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/bj.jpg
--------------------------------------------------------------------------------
/static/img/brasil.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/brasil.jpg
--------------------------------------------------------------------------------
/static/img/colombia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/colombia.jpg
--------------------------------------------------------------------------------
/static/img/cor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/cor.png
--------------------------------------------------------------------------------
/static/img/costarica.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/costarica.jpg
--------------------------------------------------------------------------------
/static/img/croacia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/croacia.jpg
--------------------------------------------------------------------------------
/static/img/egypt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/egypt.jpg
--------------------------------------------------------------------------------
/static/img/england.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/england.jpg
--------------------------------------------------------------------------------
/static/img/fcb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/fcb.jpg
--------------------------------------------------------------------------------
/static/img/fla.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/fla.png
--------------------------------------------------------------------------------
/static/img/france.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/france.jpg
--------------------------------------------------------------------------------
/static/img/germany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/germany.png
--------------------------------------------------------------------------------
/static/img/iceland.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/iceland.jpg
--------------------------------------------------------------------------------
/static/img/juv.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/juv.jpg
--------------------------------------------------------------------------------
/static/img/mc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/mc.jpg
--------------------------------------------------------------------------------
/static/img/mexico.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/mexico.jpg
--------------------------------------------------------------------------------
/static/img/mil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/mil.png
--------------------------------------------------------------------------------
/static/img/mu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/mu.png
--------------------------------------------------------------------------------
/static/img/peru.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/peru.jpg
--------------------------------------------------------------------------------
/static/img/rm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/rm.png
--------------------------------------------------------------------------------
/static/img/rp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/rp.png
--------------------------------------------------------------------------------
/static/img/sale-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/sale-icon.png
--------------------------------------------------------------------------------
/static/img/senegal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/senegal.jpg
--------------------------------------------------------------------------------
/static/img/spain.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/spain.jpg
--------------------------------------------------------------------------------
/static/img/sweden.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/sweden.jpg
--------------------------------------------------------------------------------
/static/img/uruguay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mariobox/flask-ecomm/5c11d61c3e0b40d0706b98a5f8ccea3fb438f4d7/static/img/uruguay.png
--------------------------------------------------------------------------------
/static/js/myscripts.js:
--------------------------------------------------------------------------------
1 | $(".target").on("click", function() {
2 | let $button = $(this);
3 | let oldVal = parseInt($button.parent().find("input").val());
4 | let newVal = 0;
5 |
6 | if ($button.text() == '+') {
7 | newVal = oldVal + 1;
8 | }
9 |
10 | else {
11 | if (oldVal > 0) {
12 | newVal = oldVal - 1;
13 | }
14 | else {
15 | newVal = 0;
16 | }
17 | }
18 |
19 | $button.parent().find("input").val(newVal);
20 | });
21 |
22 |
23 |
24 |
25 |
26 | $('.addToCart').on("click", function(event) {
27 | console.log('hello');
28 | if($(this).prev().prev().prev().find("input").val() == '0') {
29 | event.preventDefault();
30 | $(this).next().next().next().html("You need to select at least one shirt.");
31 | $(this).next().next().next().css("display", "block");
32 | $(this).next().next().next().delay(3000).slideUp();
33 | }
34 |
35 | if ($(this).prev().val() == "0") {
36 | event.preventDefault();
37 | $(this).next().next().next().html("You need to log in to buy.");
38 | $(this).next().next().next().css("display", "block");
39 | $(this).next().next().next().delay(3000).slideUp();
40 | }
41 | });
42 |
43 |
44 | $(".flashMessage").delay(3000).slideUp();
45 |
46 |
--------------------------------------------------------------------------------
/static/js/validate.js:
--------------------------------------------------------------------------------
1 | // The submit button
2 | const SUBMIT = $( "#submit" );
3 |
4 | // Each of the fields and error message divs
5 | const USERNAME = $( "#username" );
6 | const USERNAME_MSG = $( "#user-msg" );
7 |
8 | const PASSWORD = $( "#password" );
9 | const PASSWORD_MSG = $( "#password-msg" );
10 |
11 | const CONFIRM = $( "#confirm" );
12 | const CONFIRM_MSG = $( "#confirm-msg" );
13 |
14 | const FNAME = $( "#fname" );
15 | const FNAME_MSG = $( "#fname-msg" );
16 |
17 | const LNAME = $( "#lname" );
18 | const LNAME_MSG = $( "#lname-msg" );
19 |
20 | const EMAIL = $( "#email" );
21 | const EMAIL_MSG = $( "#email-msg" );
22 |
23 | /**
24 | * Resets the error message fields and makes the submit
25 | * button visible.
26 | */
27 | function reset_form ( )
28 | {
29 | USERNAME_MSG.html( "" );
30 | USERNAME_MSG.hide();
31 | PASSWORD_MSG.html( "" );
32 | PASSWORD_MSG.hide();
33 | CONFIRM_MSG.html( "" );
34 | CONFIRM_MSG.hide();
35 | LNAME_MSG.html( "" );
36 | LNAME_MSG.hide();
37 | FNAME_MSG.html( "" );
38 | FNAME_MSG.hide();
39 | EMAIL_MSG.html( "" );
40 | EMAIL_MSG.hide();
41 | SUBMIT.show();
42 | }
43 |
44 | /**
45 | * Validates the information in the register form so that
46 | * the server is not required to check this information.
47 | */
48 | function validate ( )
49 | {
50 | let valid = true;
51 | reset_form ( );
52 | SUBMIT.hide();
53 |
54 | // This currently checks to see if the username is
55 | // present and if it is at least 5 characters in length.
56 | if ( !USERNAME.val() || USERNAME.val().length < 5 )
57 | {
58 | // Show an invalid input message
59 | USERNAME_MSG.html( "Username must be 5 characters or more" );
60 | USERNAME_MSG.show();
61 | // Indicate the type of bad input in the console.
62 | console.log( "Bad username" );
63 | // Indicate that the form is invalid.
64 | valid = false;
65 | }
66 | // TODO: Add your additional checks here.
67 |
68 |
69 | if ( USERNAME.val() != USERNAME.val().toLowerCase())
70 | {
71 | USERNAME_MSG.html("Username must be all lowercase");
72 | USERNAME_MSG.show();
73 | valid = false;
74 | }
75 |
76 | if ( !PASSWORD.val() || PASSWORD.val().length < 8 )
77 | {
78 | PASSWORD_MSG.html("Password needs to be at least 8 characters long");
79 | PASSWORD_MSG.show();
80 | valid = false;
81 | }
82 |
83 | if ( !CONFIRM.val() || PASSWORD.val() != CONFIRM.val() )
84 | {
85 | CONFIRM_MSG.html("Passwords don't match");
86 | CONFIRM_MSG.show();
87 | valid = false;
88 | }
89 |
90 | if ( !FNAME.val() )
91 | {
92 | FNAME_MSG.html("First name must not be empty");
93 | FNAME_MSG.show();
94 | valid = false;
95 | }
96 |
97 | if ( !LNAME.val() )
98 | {
99 | LNAME_MSG.html("Last name must not be empty");
100 | LNAME_MSG.show();
101 | valid = false;
102 | }
103 |
104 | var x = EMAIL.val().trim();
105 | var atpos = x.indexOf("@");
106 | var dotpos = x.lastIndexOf(".");
107 | if ( atpos < 1 || dotpos < atpos + 2 || dotpos + 2 >= x.length ) {
108 | EMAIL_MSG.html("You need to enter a valid email address");
109 | EMAIL_MSG.show();
110 | valid = false;
111 | }
112 |
113 | // If the form is valid, reset error messages
114 | if ( valid )
115 | {
116 | reset_form ( );
117 | }
118 | }
119 |
120 | // Bind the validate function to the required events.
121 | $(document).ready ( validate );
122 | USERNAME.change ( validate );
123 | PASSWORD.change ( validate );
124 | CONFIRM.change ( validate );
125 | LNAME.change ( validate );
126 | FNAME.change ( validate );
127 | EMAIL.change ( validate );
128 |
129 |
130 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Items you've bought in the past.
13 |{{ msg }}
32 |{{msg}}
32 | 42 |