diff --git a/.gitignore b/.gitignore index 6db3c9a5bc..2efb468fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,14 @@ # Ignore bundler config. /.bundle +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + # Ignore all logfiles and tempfiles. /log/* !/log/.keep /tmp -.DS_Store +.env +coverage diff --git a/Gemfile b/Gemfile index c69f4ddde9..e65d9f0c03 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,8 @@ source 'https://rubygems.org' -ruby '2.3.1' + # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '4.2.6' -# Use postgresql as the database for Active Record -# gem 'pg', '~> 0.15' +gem 'rails', '4.2.7' # Use SCSS for stylesheets gem 'sass-rails', '~> 5.0' # Use Uglifier as compressor for JavaScript assets @@ -14,6 +12,11 @@ gem 'coffee-rails', '~> 4.1.0' # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby +# Foundation Rails Gem +gem 'foundation-rails' +# Required due to an incompatibility between TurboLinks and Foundation. +gem 'jquery-turbolinks' + # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks @@ -23,6 +26,10 @@ gem 'jbuilder', '~> 2.0' # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', '~> 0.4.0', group: :doc +gem "omniauth" +gem "omniauth-github" + + # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' @@ -31,10 +38,22 @@ gem 'sdoc', '~> 0.4.0', group: :doc # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'simplecov', :require => false, :group => :test group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' + gem 'dotenv-rails' + gem 'better_errors' + gem 'binding_of_caller' + gem 'pry' + gem 'sqlite3' + +end + +group :production do + gem 'pg' + gem 'rails_12factor' end group :development do @@ -43,4 +62,6 @@ group :development do # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' + + gem "rails-erd" end diff --git a/Gemfile.lock b/Gemfile.lock index 20975578b8..9adeb061c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,46 +1,56 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.6) - actionpack (= 4.2.6) - actionview (= 4.2.6) - activejob (= 4.2.6) + actionmailer (4.2.7) + actionpack (= 4.2.7) + actionview (= 4.2.7) + activejob (= 4.2.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.6) - actionview (= 4.2.6) - activesupport (= 4.2.6) + actionpack (4.2.7) + actionview (= 4.2.7) + activesupport (= 4.2.7) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.6) - activesupport (= 4.2.6) + actionview (4.2.7) + activesupport (= 4.2.7) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (4.2.6) - activesupport (= 4.2.6) + activejob (4.2.7) + activesupport (= 4.2.7) globalid (>= 0.3.0) - activemodel (4.2.6) - activesupport (= 4.2.6) + activemodel (4.2.7) + activesupport (= 4.2.7) builder (~> 3.1) - activerecord (4.2.6) - activemodel (= 4.2.6) - activesupport (= 4.2.6) + activerecord (4.2.7) + activemodel (= 4.2.7) + activesupport (= 4.2.7) arel (~> 6.0) - activesupport (4.2.6) + activesupport (4.2.7) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) arel (6.0.3) + babel-source (5.8.35) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) + execjs (~> 2.0) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) - byebug (8.2.5) + byebug (9.0.6) + choice (0.2.0) + coderay (1.1.1) coffee-rails (4.1.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.1.x) @@ -48,46 +58,85 @@ GEM coffee-script-source execjs coffee-script-source (1.10.0) - concurrent-ruby (1.0.1) + concurrent-ruby (1.0.2) debug_inspector (0.0.2) + docile (1.1.5) + dotenv (2.1.1) + dotenv-rails (2.1.1) + dotenv (= 2.1.1) + railties (>= 4.0, < 5.1) erubis (2.7.0) - execjs (2.6.0) - globalid (0.3.6) + execjs (2.7.0) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) + foundation-rails (6.2.4.0) + railties (>= 3.1.0) + sass (>= 3.3.0, < 3.5) + sprockets-es6 (>= 0.9.0) + globalid (0.3.7) activesupport (>= 4.1.0) + hashie (3.4.6) i18n (0.7.0) - jbuilder (2.4.1) + jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) - jquery-rails (4.1.1) + jquery-rails (4.2.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + jquery-turbolinks (2.1.0) + railties (>= 3.1.0) + turbolinks json (1.8.3) + jwt (1.5.6) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) - mime-types (3.0) + method_source (0.8.2) + mime-types (3.1) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0221) - mini_portile2 (2.0.0) - minitest (5.8.4) - multi_json (1.11.3) - nokogiri (1.6.7.2) - mini_portile2 (~> 2.0.0.rc2) + mime-types-data (3.2016.0521) + mini_portile2 (2.1.0) + minitest (5.9.1) + multi_json (1.12.1) + multi_xml (0.5.5) + multipart-post (2.0.0) + nokogiri (1.6.8.1) + mini_portile2 (~> 2.1.0) + oauth2 (1.2.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.3.1) + hashie (>= 1.2, < 4) + rack (>= 1.0, < 3) + omniauth-github (1.1.2) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) + pg (0.19.0) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) rack (1.6.4) rack-test (0.6.3) rack (>= 1.0) - rails (4.2.6) - actionmailer (= 4.2.6) - actionpack (= 4.2.6) - actionview (= 4.2.6) - activejob (= 4.2.6) - activemodel (= 4.2.6) - activerecord (= 4.2.6) - activesupport (= 4.2.6) + rails (4.2.7) + actionmailer (= 4.2.7) + actionpack (= 4.2.7) + actionview (= 4.2.7) + activejob (= 4.2.7) + activemodel (= 4.2.7) + activerecord (= 4.2.7) + activesupport (= 4.2.7) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.6) + railties (= 4.2.7) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) @@ -95,42 +144,66 @@ GEM activesupport (>= 4.2.0.beta, < 5.0) nokogiri (~> 1.6.0) rails-deprecated_sanitizer (>= 1.0.1) + rails-erd (1.5.0) + activerecord (>= 3.2) + activesupport (>= 3.2) + choice (~> 0.2.0) + ruby-graphviz (~> 1.2) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (4.2.6) - actionpack (= 4.2.6) - activesupport (= 4.2.6) + rails_12factor (0.0.3) + rails_serve_static_assets + rails_stdout_logging + rails_serve_static_assets (0.0.5) + rails_stdout_logging (0.0.5) + railties (4.2.7) + actionpack (= 4.2.7) + activesupport (= 4.2.7) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (11.1.2) + rake (11.3.0) rdoc (4.2.2) json (~> 1.4) + ruby-graphviz (1.2.2) sass (3.4.22) - sass-rails (5.0.4) - railties (>= 4.0.0, < 5.0) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sdoc (0.4.1) + sdoc (0.4.2) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) - spring (1.7.1) - sprockets (3.6.0) + simplecov (0.12.0) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) + slop (3.6.0) + spring (2.0.0) + activesupport (>= 4.2) + sprockets (3.7.0) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.0.4) + sprockets-es6 (0.9.2) + babel-source (>= 5.8.11) + babel-transpiler + sprockets (>= 3.0.0) + sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + sqlite3 (1.3.12) thor (0.19.1) thread_safe (0.3.5) - tilt (2.0.2) - turbolinks (2.5.3) - coffee-rails + tilt (2.0.5) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.0) + uglifier (3.0.3) execjs (>= 0.3.0, < 3) web-console (2.3.0) activemodel (>= 4.0) @@ -142,20 +215,30 @@ PLATFORMS ruby DEPENDENCIES + better_errors + binding_of_caller byebug coffee-rails (~> 4.1.0) + dotenv-rails + foundation-rails jbuilder (~> 2.0) jquery-rails - rails (= 4.2.6) + jquery-turbolinks + omniauth + omniauth-github + pg + pry + rails (= 4.2.7) + rails-erd + rails_12factor sass-rails (~> 5.0) sdoc (~> 0.4.0) + simplecov spring + sqlite3 turbolinks uglifier (>= 1.3.0) web-console (~> 2.0) -RUBY VERSION - ruby 2.3.1p112 - BUNDLED WITH - 1.13.5 + 1.13.6 diff --git a/README.md b/README.md deleted file mode 100644 index 220a0423c4..0000000000 --- a/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# bEtsy -[b]Etsy will be an online store where a wide variety of products can be listed and sold by any user. In this project we will focus on reinforcing the major components of Rails, Model Validation, as well as introducing some more complex logic such as user authentication. - -## Project Learning Goals -- Core comprehension of: - - Routes - - Controllers - - Models - - Views -- User based application logic -- User authentication -- Agile practices -- Feature branch management with Git -- Group project ownership - -## Guidelines -- Groups of three or four will collaborate in pairs or individually and will report to their assigned Project Manager (one of the instructors) -- Use a task manager like [Trello](http://trello.com) to track your team's efforts -- Build a logical user-flow that moves across multiple controllers and models -- Use HTML/CSS to style your website - -### Restrictions -- Do not use gems for user authentication (such as Devise) -- You must use oAuth for user authentication. - -## Getting Started -1. As a group decide on an app name (this may help lead the aesthetic) -1. As a group decide on a team name (this will amuse your instructors) -1. Have one person on your team fork/clone the project master as per usual - 1. Add all other team members as collaborators - 1. Each team member should clone the repo to their computer -1. Figure out your workflow for the project, re: Git and Task management - 1. Determine who will be the Stand Up Leader and Task Leader for the first week -1. Create a Trello board and ensure that all team members and instructors have access -1. Review the User Stories below and create Trello tasks to represent them -1. Slack your team name, app name, and link to your trello board to your Project Manager - -## Expectations -Build an online system for listing, selling, reviewing, and buying a wide variety of products listed by multiple merchants. - -### General Requirements -- Unit tests and/or specs for - - Controllers - - Models - - Routes -- Test code coverage (using SimpleCov - remember me!) - - 90% for all controller and model classes - -### User Stories -#### Guest User (Unauthenticated) -As a guest to the website (not signed in) I **can**: - -- Browse all products -- Browse products by category -- Browse products by merchant (users) -- View any individual product with additional details -- Leave a review for a product providing: - - A Text review - - A rating out of 5 -- Add in-stock products to my cart -- Remove products from my cart -- Change the quantity of an existing product in my cart -- Purchase the items in my cart, providing: - - Email Address - - Mailing Address - - Name on credit card - - Credit card number - - Credit cart expiration - - Credit Card CVV (security code) - - Billing zip code -- Purchasing an order makes the following changes: - - Reduces the number of inventory for each product - - Changes the order state from "pending" to "paid" - - Clears the current cart -- After purchasing an order, I can view a confirmation screen including: - - Each item in the order with a quantity and line-item subtotal - - A link to the item description page - - Order total price - - DateTime the order was placed - - The current status of the order -- Sign up to be a merchant using OAuth - - Every merchant must have a username -- Sign in to my merchant account using OAuth - -As a guest I **cannot**: - -- Add products to the cart that are out of stock -- View any link or page to manage any products -- View any of the account pages - -#### Authenticated Users -As a signed-in user, I **can**: - -- Do everything a guest user can do except for sign up and sign in -- Sign out -- Create new categories (categories are shared between all merchants) -- Create a new product providing: - - name - - description - - price - - photo URL - - stock -- Assign my products to any number of categories -- Retire a product from being sold, which hides it from browsing -- View an account page to edit/update my existing products -- View an account page showing my order fulfillment -- On the order fulfillment page: - - Total Revenue - - Total Revenue by status - - Total number of orders by status - - Filter orders displayed by status - - Link to each individual order - - A list of orders including at least one of my products: - - Each order item sold by me with a quantity and line-item subtotal - - A link to the item description page - - DateTime the order was placed - - Link to transition the order item to marked as shipped - - The current status of the order ("pending", "paid", "complete", "cancelled") -- View an individual order to see the user's: - - Name - - Email address - - Mailing address - - Last four digits of their credit card - - Credit card expiration date - -As a signed-in user, I **cannot**: - -- Review my own products -- View order items from a shared order that belong to another merchant -- View another users private data (i.e. order fulfillment or product management) - -### Model Validations -Many of our models will have attributes that are required for our application to use and display data consistently. Each model will have attributes with requirements for a valid record. The requirements are summarized below: - -#### Merchant -- Username must be present -- Username must be unique -- Email Address must be present -- Email Address must be unique - -#### Product -- Name must be present -- Name must be unique -- Price must be present -- Price must be a number -- Price must be greater than 0 -- Product must belong to a User - -#### Order -- An Order must have one or more Order Items - -#### OrderItem -- Must belong to a Product -- Must belong to an Order -- Quantity must be present -- Quantity must be an integer -- Quantity must be greater than 0 - -#### Review -- Rating must be present -- Rating must be an integer -- Rating must be between 1 and 5 - -## Submission Guidelines -Your final project must be deployed to [Heroku](http://heroku.com). Your team will open a single pull request for the entire project. Include the link to your Heroku deployment in the PR's description, as well as the team name and the names of all members. - -## Team Leaders -Each team will have team leaders who are responsible for keeping track of each team member's contributions. Rotate leader roles at the beginning of each week; every team member should be in at least one leader role during the project. - -- Stand Up Leader - - Notifies team members about meeting schedule and ensures that everyone is present and ready - - Takes notes about each person's daily report in Stand Up - - Keeps the meeting moving -- Task Leader - - Leads discussion on task assignment - - Decide if a task should be completed alone or in a pair - - Assign tasks based on... - - Individual comfort - - Desire - - Ability - - Ensures the task list stays up to date - -## Stand Up Meetings -The Project Manager for your team will determine the timing for all Stand Up meetings. Because PMs are managing multiple projects at once this time will be different for each team, and may change from day to day. The meeting schedule will be communicated to the Stand Up Leader as soon as it is determined. - -At the end of each day, your team's assigned Project Manager will review the Trello board for all tasks discussed during that day's Stand Up meeting. - -## Weekly Demos -In a real world work environment, a team's success is measured by their product as opposed to each individual's contribution. - -Each team will present their progress and respond to questions from their Project Manager each Friday. Every team member will participate in these demos; the PM will ask specific questions regarding -1. The team's progress and plan for completing the project -1. The technical decisions and implementation -1. Every team member's understanding of the underlying technical structures. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000000..dd4e97e22e --- /dev/null +++ b/README.rdoc @@ -0,0 +1,28 @@ +== README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... + + +Please feel free to use a different markup language if you do not plan to run +rake doc:app. diff --git a/app/assets/images/alligator.jpeg b/app/assets/images/alligator.jpeg new file mode 100644 index 0000000000..716dd24c1d Binary files /dev/null and b/app/assets/images/alligator.jpeg differ diff --git a/app/assets/images/baboon.jpeg b/app/assets/images/baboon.jpeg new file mode 100644 index 0000000000..b4529ab8ed Binary files /dev/null and b/app/assets/images/baboon.jpeg differ diff --git a/app/assets/images/bananas.jpeg b/app/assets/images/bananas.jpeg new file mode 100644 index 0000000000..e38ae62dc4 Binary files /dev/null and b/app/assets/images/bananas.jpeg differ diff --git a/app/assets/images/bearded_dragon.jpeg b/app/assets/images/bearded_dragon.jpeg new file mode 100644 index 0000000000..49c4c69fd4 Binary files /dev/null and b/app/assets/images/bearded_dragon.jpeg differ diff --git a/app/assets/images/big_catnip.jpeg b/app/assets/images/big_catnip.jpeg new file mode 100644 index 0000000000..087647c5c6 Binary files /dev/null and b/app/assets/images/big_catnip.jpeg differ diff --git a/app/assets/images/big_cats_book.jpeg b/app/assets/images/big_cats_book.jpeg new file mode 100644 index 0000000000..ca00abdf36 Binary files /dev/null and b/app/assets/images/big_cats_book.jpeg differ diff --git a/app/assets/images/boots.jpeg b/app/assets/images/boots.jpeg new file mode 100644 index 0000000000..57da99b95c Binary files /dev/null and b/app/assets/images/boots.jpeg differ diff --git a/app/assets/images/capuchin.jpeg b/app/assets/images/capuchin.jpeg new file mode 100644 index 0000000000..46acdb7c34 Binary files /dev/null and b/app/assets/images/capuchin.jpeg differ diff --git a/app/assets/images/chameleon.jpeg b/app/assets/images/chameleon.jpeg new file mode 100644 index 0000000000..4d9bfc4e7d Binary files /dev/null and b/app/assets/images/chameleon.jpeg differ diff --git a/app/assets/images/cheetah.jpeg b/app/assets/images/cheetah.jpeg new file mode 100644 index 0000000000..63d1419986 Binary files /dev/null and b/app/assets/images/cheetah.jpeg differ diff --git a/app/assets/images/chimpanzee.jpeg b/app/assets/images/chimpanzee.jpeg new file mode 100644 index 0000000000..e557e1d52b Binary files /dev/null and b/app/assets/images/chimpanzee.jpeg differ diff --git a/app/assets/images/crocodile.jpeg b/app/assets/images/crocodile.jpeg new file mode 100644 index 0000000000..5d56b23797 Binary files /dev/null and b/app/assets/images/crocodile.jpeg differ diff --git a/app/assets/images/cymbals.jpeg b/app/assets/images/cymbals.jpeg new file mode 100644 index 0000000000..124a867d4a Binary files /dev/null and b/app/assets/images/cymbals.jpeg differ diff --git a/app/assets/images/dwarf_caiman.jpeg b/app/assets/images/dwarf_caiman.jpeg new file mode 100644 index 0000000000..b27457234c Binary files /dev/null and b/app/assets/images/dwarf_caiman.jpeg differ diff --git a/app/assets/images/elephant.jpeg b/app/assets/images/elephant.jpeg new file mode 100644 index 0000000000..cf6b26ee5c Binary files /dev/null and b/app/assets/images/elephant.jpeg differ diff --git a/app/assets/images/eucalyptus_leaves.jpeg b/app/assets/images/eucalyptus_leaves.jpeg new file mode 100644 index 0000000000..289c12ba66 Binary files /dev/null and b/app/assets/images/eucalyptus_leaves.jpeg differ diff --git a/app/assets/images/falabella.jpeg b/app/assets/images/falabella.jpeg new file mode 100644 index 0000000000..7cfe5c0344 Binary files /dev/null and b/app/assets/images/falabella.jpeg differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100755 index 0000000000..86da0d1cd6 Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/images/giant_ball.jpeg b/app/assets/images/giant_ball.jpeg new file mode 100644 index 0000000000..1fd97a3a24 Binary files /dev/null and b/app/assets/images/giant_ball.jpeg differ diff --git a/app/assets/images/giraffe.jpeg b/app/assets/images/giraffe.jpeg new file mode 100644 index 0000000000..1887cd61d8 Binary files /dev/null and b/app/assets/images/giraffe.jpeg differ diff --git a/app/assets/images/harness.jpeg b/app/assets/images/harness.jpeg new file mode 100644 index 0000000000..100485c722 Binary files /dev/null and b/app/assets/images/harness.jpeg differ diff --git a/app/assets/images/hat.jpeg b/app/assets/images/hat.jpeg new file mode 100644 index 0000000000..7e4e9d5bab Binary files /dev/null and b/app/assets/images/hat.jpeg differ diff --git a/app/assets/images/hippopotamus.jpeg b/app/assets/images/hippopotamus.jpeg new file mode 100644 index 0000000000..c49c5cd597 Binary files /dev/null and b/app/assets/images/hippopotamus.jpeg differ diff --git a/app/assets/images/koala.jpeg b/app/assets/images/koala.jpeg new file mode 100644 index 0000000000..49bf0e32ea Binary files /dev/null and b/app/assets/images/koala.jpeg differ diff --git a/app/assets/images/leash.jpeg b/app/assets/images/leash.jpeg new file mode 100644 index 0000000000..d73637c811 Binary files /dev/null and b/app/assets/images/leash.jpeg differ diff --git a/app/assets/images/lion.jpeg b/app/assets/images/lion.jpeg new file mode 100644 index 0000000000..79ef1cef00 Binary files /dev/null and b/app/assets/images/lion.jpeg differ diff --git a/app/assets/images/marsupials_book.jpeg b/app/assets/images/marsupials_book.jpeg new file mode 100644 index 0000000000..74b2799e3f Binary files /dev/null and b/app/assets/images/marsupials_book.jpeg differ diff --git a/app/assets/images/micro_pig.jpeg b/app/assets/images/micro_pig.jpeg new file mode 100644 index 0000000000..180c916623 Binary files /dev/null and b/app/assets/images/micro_pig.jpeg differ diff --git a/app/assets/images/monkey_book.jpeg b/app/assets/images/monkey_book.jpeg new file mode 100644 index 0000000000..613f9822e5 Binary files /dev/null and b/app/assets/images/monkey_book.jpeg differ diff --git a/app/assets/images/poop_scoop.jpeg b/app/assets/images/poop_scoop.jpeg new file mode 100644 index 0000000000..9fc9006297 Binary files /dev/null and b/app/assets/images/poop_scoop.jpeg differ diff --git a/app/assets/images/pygmy_goat.jpeg b/app/assets/images/pygmy_goat.jpeg new file mode 100644 index 0000000000..fe8dbcf4b3 Binary files /dev/null and b/app/assets/images/pygmy_goat.jpeg differ diff --git a/app/assets/images/pygmy_hedgehog.jpeg b/app/assets/images/pygmy_hedgehog.jpeg new file mode 100644 index 0000000000..4edd780304 Binary files /dev/null and b/app/assets/images/pygmy_hedgehog.jpeg differ diff --git a/app/assets/images/quokka.jpeg b/app/assets/images/quokka.jpeg new file mode 100644 index 0000000000..0a29c25bee Binary files /dev/null and b/app/assets/images/quokka.jpeg differ diff --git a/app/assets/images/reptile_book.jpeg b/app/assets/images/reptile_book.jpeg new file mode 100644 index 0000000000..931466da88 Binary files /dev/null and b/app/assets/images/reptile_book.jpeg differ diff --git a/app/assets/images/rhesus_macaque.jpeg b/app/assets/images/rhesus_macaque.jpeg new file mode 100644 index 0000000000..f8c054b8b9 Binary files /dev/null and b/app/assets/images/rhesus_macaque.jpeg differ diff --git a/app/assets/images/scratching_post.jpeg b/app/assets/images/scratching_post.jpeg new file mode 100644 index 0000000000..a7965a8f1f Binary files /dev/null and b/app/assets/images/scratching_post.jpeg differ diff --git a/app/assets/images/small_crate.jpeg b/app/assets/images/small_crate.jpeg new file mode 100644 index 0000000000..1cac28e3f4 Binary files /dev/null and b/app/assets/images/small_crate.jpeg differ diff --git a/app/assets/images/snow_leopard.jpeg b/app/assets/images/snow_leopard.jpeg new file mode 100644 index 0000000000..bb84f33283 Binary files /dev/null and b/app/assets/images/snow_leopard.jpeg differ diff --git a/app/assets/images/squirrel_monkey.jpeg b/app/assets/images/squirrel_monkey.jpeg new file mode 100644 index 0000000000..ecb56002a1 Binary files /dev/null and b/app/assets/images/squirrel_monkey.jpeg differ diff --git a/app/assets/images/tasmanian_devil.jpeg b/app/assets/images/tasmanian_devil.jpeg new file mode 100644 index 0000000000..ad3df344d1 Binary files /dev/null and b/app/assets/images/tasmanian_devil.jpeg differ diff --git a/app/assets/images/tiger.jpeg b/app/assets/images/tiger.jpeg new file mode 100644 index 0000000000..073a04beb2 Binary files /dev/null and b/app/assets/images/tiger.jpeg differ diff --git a/app/assets/images/wallaby.jpeg b/app/assets/images/wallaby.jpeg new file mode 100644 index 0000000000..8ac9a71635 Binary files /dev/null and b/app/assets/images/wallaby.jpeg differ diff --git a/app/assets/images/zebra.jpeg b/app/assets/images/zebra.jpeg new file mode 100644 index 0000000000..7065ac6f76 Binary files /dev/null and b/app/assets/images/zebra.jpeg differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e07c5a830f..038d8d7ecc 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,5 +12,15 @@ // //= require jquery //= require jquery_ujs +//= require foundation //= require turbolinks //= require_tree . + +$(function(){ $(document).foundation(); }); + +//= require foundation +$(document).foundation(); + +$(document).on('turbolinks:load', function() { + $(function(){ $(document).foundation(); }); +}); diff --git a/app/assets/javascripts/carts.coffee b/app/assets/javascripts/carts.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/carts.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss new file mode 100644 index 0000000000..8a3527f8c6 --- /dev/null +++ b/app/assets/stylesheets/_settings.scss @@ -0,0 +1,573 @@ +// Foundation for Sites Settings +// ----------------------------- +// +// Table of Contents: +// +// 1. Global +// 2. Breakpoints +// 3. The Grid +// 4. Base Typography +// 5. Typography Helpers +// 6. Abide +// 7. Accordion +// 8. Accordion Menu +// 9. Badge +// 10. Breadcrumbs +// 11. Button +// 12. Button Group +// 13. Callout +// 14. Close Button +// 15. Drilldown +// 16. Dropdown +// 17. Dropdown Menu +// 18. Flex Video +// 19. Forms +// 20. Label +// 21. Media Object +// 22. Menu +// 23. Meter +// 24. Off-canvas +// 25. Orbit +// 26. Pagination +// 27. Progress Bar +// 28. Reveal +// 29. Slider +// 30. Switch +// 31. Table +// 32. Tabs +// 33. Thumbnail +// 34. Title Bar +// 35. Tooltip +// 36. Top Bar + +@import 'util/util'; + +// 1. Global +// --------- + +$global-font-size: 100%; +$global-width: rem-calc(1200); +$global-lineheight: 1.5; +$foundation-palette: ( + primary: #2199e8, + secondary: #777, + success: #3adb76, + warning: #ffae00, + alert: #ec5840, +); +$light-gray: #e6e6e6; +$medium-gray: #cacaca; +$dark-gray: #8a8a8a; +$black: #0a0a0a; +$white: #fefefe; +$body-background: $white; +$body-font-color: $black; +$body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; +$body-antialiased: true; +$global-margin: 1rem; +$global-padding: 1rem; +$global-weight-normal: normal; +$global-weight-bold: bold; +$global-radius: 0; +$global-text-direction: ltr; +$global-flexbox: false; +$print-transparent-backgrounds: true; + +@include add-foundation-colors; + +// 2. Breakpoints +// -------------- + +$breakpoints: ( + small: 0, + medium: 640px, + large: 1024px, + xlarge: 1200px, + xxlarge: 1440px, +); +$breakpoint-classes: (small medium large); + +// 3. The Grid +// ----------- + +$grid-row-width: $global-width; +$grid-column-count: 12; +$grid-column-gutter: ( + small: 20px, + medium: 30px, +); +$grid-column-align-edge: true; +$block-grid-max: 8; + +// 4. Base Typography +// ------------------ + +$header-font-family: $body-font-family; +$header-font-weight: $global-weight-normal; +$header-font-style: normal; +$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace; +$header-sizes: ( + small: ( + 'h1': 24, + 'h2': 20, + 'h3': 19, + 'h4': 18, + 'h5': 17, + 'h6': 16, + ), + medium: ( + 'h1': 48, + 'h2': 40, + 'h3': 31, + 'h4': 25, + 'h5': 20, + 'h6': 16, + ), +); +$header-color: inherit; +$header-lineheight: 1.4; +$header-margin-bottom: 0.5rem; +$header-text-rendering: optimizeLegibility; +$small-font-size: 80%; +$header-small-font-color: $medium-gray; +$paragraph-lineheight: 1.6; +$paragraph-margin-bottom: 1rem; +$paragraph-text-rendering: optimizeLegibility; +$code-color: $black; +$code-font-family: $font-family-monospace; +$code-font-weight: $global-weight-normal; +$code-background: $light-gray; +$code-border: 1px solid $medium-gray; +$code-padding: rem-calc(2 5 1); +$anchor-color: $primary-color; +$anchor-color-hover: scale-color($anchor-color, $lightness: -14%); +$anchor-text-decoration: none; +$anchor-text-decoration-hover: none; +$hr-width: $global-width; +$hr-border: 1px solid $medium-gray; +$hr-margin: rem-calc(20) auto; +$list-lineheight: $paragraph-lineheight; +$list-margin-bottom: $paragraph-margin-bottom; +$list-style-type: disc; +$list-style-position: outside; +$list-side-margin: 1.25rem; +$list-nested-side-margin: 1.25rem; +$defnlist-margin-bottom: 1rem; +$defnlist-term-weight: $global-weight-bold; +$defnlist-term-margin-bottom: 0.3rem; +$blockquote-color: $dark-gray; +$blockquote-padding: rem-calc(9 20 0 19); +$blockquote-border: 1px solid $medium-gray; +$cite-font-size: rem-calc(13); +$cite-color: $dark-gray; +$keystroke-font: $font-family-monospace; +$keystroke-color: $black; +$keystroke-background: $light-gray; +$keystroke-padding: rem-calc(2 4 0); +$keystroke-radius: $global-radius; +$abbr-underline: 1px dotted $black; + +// 5. Typography Helpers +// --------------------- + +$lead-font-size: $global-font-size * 1.25; +$lead-lineheight: 1.6; +$subheader-lineheight: 1.4; +$subheader-color: $dark-gray; +$subheader-font-weight: $global-weight-normal; +$subheader-margin-top: 0.2rem; +$subheader-margin-bottom: 0.5rem; +$stat-font-size: 2.5rem; + +// 6. Abide +// -------- + +$abide-inputs: true; +$abide-labels: true; +$input-background-invalid: map-get($foundation-palette, alert); +$form-label-color-invalid: map-get($foundation-palette, alert); +$input-error-color: map-get($foundation-palette, alert); +$input-error-font-size: rem-calc(12); +$input-error-font-weight: $global-weight-bold; + +// 7. Accordion +// ------------ + +$accordion-background: $white; +$accordion-plusminus: true; +$accordion-item-color: foreground($accordion-background, $primary-color); +$accordion-item-background-hover: $light-gray; +$accordion-item-padding: 1.25rem 1rem; +$accordion-content-background: $white; +$accordion-content-border: 1px solid $light-gray; +$accordion-content-color: foreground($accordion-content-background, $body-font-color); +$accordion-content-padding: 1rem; + +// 8. Accordion Menu +// ----------------- + +$accordionmenu-arrows: false; +$accordionmenu-arrow-color: $primary-color; + +// 9. Badge +// -------- + +$badge-background: $primary-color; +$badge-color: foreground($badge-background); +$badge-padding: 0.3em; +$badge-minwidth: 2.1em; +$badge-font-size: 0.6rem; + +// 10. Breadcrumbs +// --------------- + +$breadcrumbs-margin: 0 0 $global-margin 0; +$breadcrumbs-item-font-size: rem-calc(11); +$breadcrumbs-item-color: $primary-color; +$breadcrumbs-item-color-current: $black; +$breadcrumbs-item-color-disabled: $medium-gray; +$breadcrumbs-item-margin: 0.75rem; +$breadcrumbs-item-uppercase: true; +$breadcrumbs-item-slash: true; + +// 11. Button +// ---------- + +$button-padding: 0.85em 1em; +$button-margin: 0 0 $global-margin 0; +$button-fill: solid; +$button-background: $primary-color; +$button-background-hover: scale-color($button-background, $lightness: -15%); +$button-color: $white; +$button-color-alt: $black; +$button-radius: $global-radius; +$button-sizes: ( + tiny: 0.6rem, + small: 0.75rem, + default: 0.9rem, + large: 1.25rem, +); +$button-opacity-disabled: 0.25; +$button-background-hover-lightness: -20%; +$button-hollow-hover-lightness: -50%; + +// 12. Button Group +// ---------------- + +$buttongroup-margin: 1rem; +$buttongroup-spacing: 1px; +$buttongroup-child-selector: '.button'; +$buttongroup-expand-max: 6; + +// 13. Callout +// ----------- + +$callout-background: $white; +$callout-background-fade: 85%; +$callout-border: 1px solid rgba($black, 0.25); +$callout-margin: 0 0 1rem 0; +$callout-padding: 1rem; +$callout-font-color: $body-font-color; +$callout-font-color-alt: $body-background; +$callout-radius: $global-radius; +$callout-link-tint: 30%; + +// 14. Close Button +// ---------------- + +$closebutton-position: right top; +$closebutton-offset-horizontal: 1rem; +$closebutton-offset-vertical: 0.5rem; +$closebutton-size: 2em; +$closebutton-lineheight: 1; +$closebutton-color: $dark-gray; +$closebutton-color-hover: $black; + +// 15. Drilldown +// ------------- + +$drilldown-transition: transform 0.15s linear; +$drilldown-arrows: true; +$drilldown-arrow-color: $primary-color; +$drilldown-background: $white; + +// 16. Dropdown +// ------------ + +$dropdown-padding: 1rem; +$dropdown-border: 1px solid $medium-gray; +$dropdown-font-size: 1rem; +$dropdown-width: 300px; +$dropdown-radius: $global-radius; +$dropdown-sizes: ( + tiny: 100px, + small: 200px, + large: 400px, +); + +// 17. Dropdown Menu +// ----------------- + +$dropdownmenu-arrows: true; +$dropdownmenu-arrow-color: $anchor-color; +$dropdownmenu-min-width: 200px; +$dropdownmenu-background: $white; +$dropdownmenu-border: 1px solid $medium-gray; + +// 18. Flex Video +// -------------- + +$flexvideo-margin-bottom: rem-calc(16); +$flexvideo-ratio: 4 by 3; +$flexvideo-ratio-widescreen: 16 by 9; + +// 19. Forms +// --------- + +$fieldset-border: 1px solid $medium-gray; +$fieldset-padding: rem-calc(20); +$fieldset-margin: rem-calc(18 0); +$legend-padding: rem-calc(0 3); +$form-spacing: rem-calc(16); +$helptext-color: $black; +$helptext-font-size: rem-calc(13); +$helptext-font-style: italic; +$input-prefix-color: $black; +$input-prefix-background: $light-gray; +$input-prefix-border: 1px solid $medium-gray; +$input-prefix-padding: 1rem; +$form-label-color: $black; +$form-label-font-size: rem-calc(14); +$form-label-font-weight: $global-weight-normal; +$form-label-line-height: 1.8; +$select-background: $white; +$select-triangle-color: $dark-gray; +$select-radius: $global-radius; +$input-color: $black; +$input-placeholder-color: $medium-gray; +$input-font-family: inherit; +$input-font-size: rem-calc(16); +$input-background: $white; +$input-background-focus: $white; +$input-background-disabled: $light-gray; +$input-border: 1px solid $medium-gray; +$input-border-focus: 1px solid $dark-gray; +$input-shadow: inset 0 1px 2px rgba($black, 0.1); +$input-shadow-focus: 0 0 5px $medium-gray; +$input-cursor-disabled: not-allowed; +$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out; +$input-number-spinners: true; +$input-radius: $global-radius; +$button-radius: $global-radius; + +// 20. Label +// --------- + +$label-background: $primary-color; +$label-color: foreground($label-background); +$label-font-size: 0.8rem; +$label-padding: 0.33333rem 0.5rem; +$label-radius: $global-radius; + +// 21. Media Object +// ---------------- + +$mediaobject-margin-bottom: $global-margin; +$mediaobject-section-padding: $global-padding; +$mediaobject-image-width-stacked: 100%; + +// 22. Menu +// -------- + +$menu-margin: 0; +$menu-margin-nested: 1rem; +$menu-item-padding: 0.7rem 1rem; +$menu-item-color-active: $white; +$menu-item-background-active: map-get($foundation-palette, primary); +$menu-icon-spacing: 0.25rem; + +// 23. Meter +// --------- + +$meter-height: 1rem; +$meter-radius: $global-radius; +$meter-background: $medium-gray; +$meter-fill-good: $success-color; +$meter-fill-medium: $warning-color; +$meter-fill-bad: $alert-color; + +// 24. Off-canvas +// -------------- + +$offcanvas-size: 250px; +$offcanvas-background: $light-gray; +$offcanvas-zindex: -1; +$offcanvas-transition-length: 0.5s; +$offcanvas-transition-timing: ease; +$offcanvas-fixed-reveal: true; +$offcanvas-exit-background: rgba($white, 0.25); +$maincontent-class: 'off-canvas-content'; +$maincontent-shadow: 0 0 10px rgba($black, 0.5); + +// 25. Orbit +// --------- + +$orbit-bullet-background: $medium-gray; +$orbit-bullet-background-active: $dark-gray; +$orbit-bullet-diameter: 1.2rem; +$orbit-bullet-margin: 0.1rem; +$orbit-bullet-margin-top: 0.8rem; +$orbit-bullet-margin-bottom: 0.8rem; +$orbit-caption-background: rgba($black, 0.5); +$orbit-caption-padding: 1rem; +$orbit-control-background-hover: rgba($black, 0.5); +$orbit-control-padding: 1rem; +$orbit-control-zindex: 10; + +// 26. Pagination +// -------------- + +$pagination-font-size: rem-calc(14); +$pagination-margin-bottom: $global-margin; +$pagination-item-color: $black; +$pagination-item-padding: rem-calc(3 10); +$pagination-item-spacing: rem-calc(1); +$pagination-radius: $global-radius; +$pagination-item-background-hover: $light-gray; +$pagination-item-background-current: $primary-color; +$pagination-item-color-current: foreground($pagination-item-background-current); +$pagination-item-color-disabled: $medium-gray; +$pagination-ellipsis-color: $black; +$pagination-mobile-items: false; +$pagination-mobile-current-item: false; +$pagination-arrows: true; + +// 27. Progress Bar +// ---------------- + +$progress-height: 1rem; +$progress-background: $medium-gray; +$progress-margin-bottom: $global-margin; +$progress-meter-background: $primary-color; +$progress-radius: $global-radius; + +// 28. Reveal +// ---------- + +$reveal-background: $white; +$reveal-width: 600px; +$reveal-max-width: $global-width; +$reveal-padding: $global-padding; +$reveal-border: 1px solid $medium-gray; +$reveal-radius: $global-radius; +$reveal-zindex: 1005; +$reveal-overlay-background: rgba($black, 0.45); + +// 29. Slider +// ---------- + +$slider-width-vertical: 0.5rem; +$slider-transition: all 0.2s ease-in-out; +$slider-height: 0.5rem; +$slider-background: $light-gray; +$slider-fill-background: $medium-gray; +$slider-handle-height: 1.4rem; +$slider-handle-width: 1.4rem; +$slider-handle-background: $primary-color; +$slider-opacity-disabled: 0.25; +$slider-radius: $global-radius; + +// 30. Switch +// ---------- + +$switch-background: $medium-gray; +$switch-background-active: $primary-color; +$switch-height: 2rem; +$switch-height-tiny: 1.5rem; +$switch-height-small: 1.75rem; +$switch-height-large: 2.5rem; +$switch-radius: $global-radius; +$switch-margin: $global-margin; +$switch-paddle-background: $white; +$switch-paddle-offset: 0.25rem; +$switch-paddle-radius: $global-radius; +$switch-paddle-transition: all 0.25s ease-out; + +// 31. Table +// --------- + +$table-background: $white; +$table-color-scale: 5%; +$table-border: 1px solid smart-scale($table-background, $table-color-scale); +$table-padding: rem-calc(8 10 10); +$table-hover-scale: 2%; +$table-row-hover: darken($table-background, $table-hover-scale); +$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale); +$table-striped-background: smart-scale($table-background, $table-color-scale); +$table-stripe: even; +$table-head-background: smart-scale($table-background, $table-color-scale / 2); +$table-head-row-hover: darken($table-head-background, $table-hover-scale); +$table-foot-background: smart-scale($table-background, $table-color-scale); +$table-foot-row-hover: darken($table-foot-background, $table-hover-scale); +$table-head-font-color: $body-font-color; +$table-foot-font-color: $body-font-color; +$show-header-for-stacked: false; + +// 32. Tabs +// -------- + +$tab-margin: 0; +$tab-background: $white; +$tab-background-active: $light-gray; +$tab-item-font-size: rem-calc(12); +$tab-item-background-hover: $white; +$tab-item-padding: 1.25rem 1.5rem; +$tab-expand-max: 6; +$tab-content-background: $white; +$tab-content-border: $light-gray; +$tab-content-color: foreground($tab-background, $primary-color); +$tab-content-padding: 1rem; + +// 33. Thumbnail +// ------------- + +$thumbnail-border: solid 4px $white; +$thumbnail-margin-bottom: $global-margin; +$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2); +$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5); +$thumbnail-transition: box-shadow 200ms ease-out; +$thumbnail-radius: $global-radius; + +// 34. Title Bar +// ------------- + +$titlebar-background: $black; +$titlebar-color: $white; +$titlebar-padding: 0.5rem; +$titlebar-text-font-weight: bold; +$titlebar-icon-color: $white; +$titlebar-icon-color-hover: $medium-gray; +$titlebar-icon-spacing: 0.25rem; + +// 35. Tooltip +// ----------- + +$has-tip-font-weight: $global-weight-bold; +$has-tip-border-bottom: dotted 1px $dark-gray; +$tooltip-background-color: $black; +$tooltip-color: $white; +$tooltip-padding: 0.75rem; +$tooltip-font-size: $small-font-size; +$tooltip-pip-width: 0.75rem; +$tooltip-pip-height: $tooltip-pip-width * 0.866; +$tooltip-radius: $global-radius; + +// 36. Top Bar +// ----------- + +$topbar-padding: 0.5rem; +$topbar-background: $light-gray; +$topbar-submenu-background: $topbar-background; +$topbar-title-spacing: 1rem; +$topbar-input-width: 200px; +$topbar-unstack-breakpoint: medium; diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css deleted file mode 100644 index f9cd5b3483..0000000000 --- a/app/assets/stylesheets/application.css +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any styles - * defined in the other CSS/SCSS files in this directory. It is generally better to create a new - * file per style scope. - * - *= require_tree . - *= require_self - */ diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss new file mode 100644 index 0000000000..026bf7ba43 --- /dev/null +++ b/app/assets/stylesheets/application.css.scss @@ -0,0 +1,130 @@ + +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any styles + * defined in the other CSS/SCSS files in this directory. It is generally better to create a new + * file per style scope. + * + *= require_tree . + *= require_self + *= require foundation_and_overrides + + */ + /*Header and footer CSS*/ + body { + margin: 0 5%; + } + + .top-bar { + border: 1px blue dashed; + margin: 2% -2%; + } + + .top-bar-left { + align-self: center; + } + + .lower-nav li { + display: inline-block; + } + + .title_area { + margin-right: 3em; + } + + .github-button, .log-out-button, .dashboard-button, .cart-button, .edit-button-show, .add-button { + color: white; + font-weight: bold; + border-radius: 5px; + padding: 5px; + } + + .github-button { + background-color: blue; + } + + .log-out-button { + background-color: red; + } + + .dashboard-button, .edit-button-show { + background-color: green; + } + + .cart-button, .add-button { + background-color: purple; + } + + .get-products-button, .retire-button { + background-color: lightgrey; + border-radius: 5px; + } + + .search-form { + display: inline-block; + } + +// All images + .rounded-image { + border-radius: 45px; + margin: 0 auto; + max-width: 85%; + max-height: 200px; + } + + .image-container { + display: block; + padding: 4px; + border: 1px solid clear; + border-radius: 4px; + } + + .product-name { + position: absolute; + bottom: 2rem; + padding: 1rem; + text-align: center; + } + + .thumbnail { + width: 300px; + height: 300px; + overflow: hidden; + margin: auto; + position: relative; + } + + .edit-button { + position: absolute; + bottom: 0; + padding: 1rem; + } + + // Products by Category page + .category-header, .product-description { + text-align: center; + } + + .dashboard-orders-table { + border: 1px dotted blue; + margin: 30px 10px; + } + + .dashboard-summary { + margin: 0 50px; + } + +.product-edit-form, .order-form, .review-form { + width: 50%; + margin: 0 auto; +} + + // .caption { + // margin-bottom: 40px; + // } diff --git a/app/assets/stylesheets/carts.scss b/app/assets/stylesheets/carts.scss new file mode 100644 index 0000000000..2fe1b9e710 --- /dev/null +++ b/app/assets/stylesheets/carts.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Carts controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/categories.scss b/app/assets/stylesheets/categories.scss new file mode 100644 index 0000000000..ef1657f8c9 --- /dev/null +++ b/app/assets/stylesheets/categories.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the categories controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/foundation_and_overrides.scss b/app/assets/stylesheets/foundation_and_overrides.scss new file mode 100644 index 0000000000..a38f03d463 --- /dev/null +++ b/app/assets/stylesheets/foundation_and_overrides.scss @@ -0,0 +1,52 @@ +@charset 'utf-8'; + +@import 'settings'; +@import 'foundation'; + +// If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package. +// +@import 'motion-ui/motion-ui'; + +// We include everything by default. To slim your CSS, remove components you don't use. + +@include foundation-global-styles; +@include foundation-grid; +@include foundation-typography; +@include foundation-button; +@include foundation-forms; +@include foundation-visibility-classes; +@include foundation-float-classes; +@include foundation-accordion; +@include foundation-accordion-menu; +@include foundation-badge; +@include foundation-breadcrumbs; +@include foundation-button-group; +@include foundation-callout; +@include foundation-close-button; +@include foundation-drilldown-menu; +@include foundation-dropdown; +@include foundation-dropdown-menu; +@include foundation-flex-video; +@include foundation-label; +@include foundation-media-object; +@include foundation-menu; +@include foundation-menu-icon; +@include foundation-off-canvas; +@include foundation-orbit; +@include foundation-pagination; +@include foundation-progress-bar; +@include foundation-slider; +@include foundation-sticky; +@include foundation-reveal; +@include foundation-switch; +@include foundation-table; +@include foundation-tabs; +@include foundation-thumbnail; +@include foundation-title-bar; +@include foundation-tooltip; +@include foundation-top-bar; + +// If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package. +// + @include motion-ui-transitions; +@include motion-ui-animations; diff --git a/app/assets/stylesheets/guests.scss b/app/assets/stylesheets/guests.scss new file mode 100644 index 0000000000..646b204ef1 --- /dev/null +++ b/app/assets/stylesheets/guests.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the guests controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/homepages.scss b/app/assets/stylesheets/homepages.scss new file mode 100644 index 0000000000..2305c36d10 --- /dev/null +++ b/app/assets/stylesheets/homepages.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Homepages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/merchants.scss b/app/assets/stylesheets/merchants.scss new file mode 100644 index 0000000000..2327c00afe --- /dev/null +++ b/app/assets/stylesheets/merchants.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the merchants controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/orderitems.scss b/app/assets/stylesheets/orderitems.scss new file mode 100644 index 0000000000..3ae284dd37 --- /dev/null +++ b/app/assets/stylesheets/orderitems.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the orderitems controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/orders.scss b/app/assets/stylesheets/orders.scss new file mode 100644 index 0000000000..3b0428a94e --- /dev/null +++ b/app/assets/stylesheets/orders.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the orders controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss new file mode 100644 index 0000000000..89e2e8db07 --- /dev/null +++ b/app/assets/stylesheets/products.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the products controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/reviews.scss b/app/assets/stylesheets/reviews.scss new file mode 100644 index 0000000000..6ea2454d26 --- /dev/null +++ b/app/assets/stylesheets/reviews.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the reviews controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000000..ed7a765d9a --- /dev/null +++ b/app/assets/stylesheets/scaffolds.scss @@ -0,0 +1,73 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + + &:visited { + color: #666; + } + + &:hover { + color: #fff; + background-color: #000; + } +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; + } + + ul li { + font-size: 12px; + list-style: square; + } +} diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 0000000000..7bef9cf826 --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/tasks.scss b/app/assets/stylesheets/tasks.scss new file mode 100644 index 0000000000..c5e7712d42 --- /dev/null +++ b/app/assets/stylesheets/tasks.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Tasks controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e1b9..66fe61c9d0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,29 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + helper_method :current_order + + + + def current_order + + if !session[:order_id].nil? + begin + Order.find(session[:order_id]) + rescue StandardError => err + render "/errors/not_found", status: :not_found + end + else + Order.new + end + end + + private + + def current_user + @current_user ||= Merchant.find_by(id: session[:merchant_id]) + end + + helper_method :current_user ## What is this? + end diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000000..239ea86997 --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,35 @@ +class CategoriesController < ApplicationController + before_action :require_login, only: [:create, :new] + + def new + @category = Category.new + end + + def show + @category = Category.find(params[:category_id]) + @products = @category.products.where(active: true) + end + + def create + @category = Category.new(category_params) + if @category.save + redirect_to(:back) + else + render :new + end + end + + private + + def category_params + params.require(:category).permit(:name) + end + + def require_login + if current_user.nil? + flash[:error] = "You must be logged in to view this section" + redirect_to login_failure_path + end + end + +end diff --git a/app/controllers/guests_controller.rb b/app/controllers/guests_controller.rb new file mode 100644 index 0000000000..d9e0a89324 --- /dev/null +++ b/app/controllers/guests_controller.rb @@ -0,0 +1,7 @@ +class GuestsController < ApplicationController + def index + @products = Product.all + @product_count = @products.length + end + +end diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb new file mode 100644 index 0000000000..e6487aa9e7 --- /dev/null +++ b/app/controllers/merchants_controller.rb @@ -0,0 +1,58 @@ +class MerchantsController < GuestsController + before_action :require_login, only: [:show] + + + # def index + # @merchant = Merchant.all + # end + + # def new + # @merchant = Merchant.new + # end + # + def create + @merchant = Merchant.new(merchant_params) + # Need to intialize number of merchants to 1 upon creation + if @merchant.save + redirect_to merchant_path(@merchant) + else + render :new + end + end + + def show + @merchant = Merchant.find(params[:id]) + @products = Product.where(merchant_id: @merchant.id).where(active: true) + + @inactive_products = Product.where(merchant_id: @merchant.id).where(active: false) if current_user + + + end + + def products + @merchants = Merchant.all + @merchant = Merchant.find(params[:merchant_id]) + @products = Product.where(merchant_id: @merchant.id).where(active: true) + + @inactive_products = Product.where(merchant_id: @merchant.id).where(active: false) if current_user + end + + private + + def merchant_params + params.require(:merchant).permit(:username, :email) + end + + def current_user + @current_user ||= Merchant.find_by(id: session[:merchant_id]) + end + + helper_method :current_user ## What is this? + + def require_login + if current_user.nil? + flash[:error] = "You must be logged in to view this section" + redirect_to login_failure_path + end + end +end diff --git a/app/controllers/order_items_controller.rb b/app/controllers/order_items_controller.rb new file mode 100644 index 0000000000..1f6055f382 --- /dev/null +++ b/app/controllers/order_items_controller.rb @@ -0,0 +1,77 @@ +class OrderItemsController < ApplicationController + before_action :is_product_in_cart, only: [:create] + def index + @order_items = current_order.order_items + @order = current_order + end + + def new + @order_item = OrderItem.new + end + + def create + + @order = current_order + + if @updated_quantity + # @current_product.quantity = @updated_quantity + item = @order.order_items.find_by(product_id: @current_product.product_id) + item.quantity = @updated_quantity + if item.save + session[:order_id] = @order.id + redirect_to order_items_path + else + redirect_to(:back) + end + else + @order_item = @order.order_items.new(order_item_params) + # @order_item.quantity = 1 if @order_item.quantity == nil + if @order_item.save + session[:order_id] = @order.id + redirect_to order_items_path + else + redirect_to :back + end + end + end + + def update + @order = current_order + @order_item = OrderItem.find(params[:id]) + @order_item.update_attributes(order_item_params) + redirect_to order_items_path + end + + def destroy + @order = current_order + @order_item = @order.order_items.find(params[:id]) + @order_item.destroy + @order_items = @order.order_items + redirect_to order_items_path + end + + def is_product_in_cart + order = current_order + @product_id = params[:order_item][:product_id] + if order.order_items.find_by(product_id: @product_id) + @current_product = order.order_items.find_by(product_id: @product_id) + @updated_quantity = @current_product.quantity + params[:order_item][:quantity].to_i + end + end + + def update_cart(product_in_cart) + current_quantity = product_in_cart.quantity + additional_quantity = params[:order_item][:quantity].to_i + params[:order_item][:quantity] = current_quantity + additional_quantity + + product_in_cart.update_attributes(order_item_params) + + redirect_to order_items_path + end + + private + + def order_item_params + params.require(:order_item).permit(:quantity, :product_id, :merchant_id) + end +end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb new file mode 100644 index 0000000000..5e75477f54 --- /dev/null +++ b/app/controllers/orders_controller.rb @@ -0,0 +1,65 @@ +class OrdersController < ApplicationController + before_action :find_order, only: [:edit, :update] + before_action :paid_order_status, only: [:update] + + # def index; end # this will show order for only that merchant. Like auth hash - takes from session + + def show + begin + @order = Order.find(params[:id]) + rescue StandardError => err + render "/errors/not_found", status: :not_found + end + + @order_items = current_order.order_items + end + + def new + @order = Order.new + end + + def create + @order = Order.new(post_params) + if @order.save + redirect_to root_path(@order) + else + render :new + end + end + + def edit + @order.order_status = "almost paid" + end + + def update + @order.order_status = "almost paid" + if @order.update(post_params) + redirect_to order_path + else + render :edit + end + end + + def destroy + @order = Order.find(params[:id]).destroy + redirect_to root_path + end + + private + + def post_params + params.require(:order).permit(:name, :email, :street_address, :city, :state, :mailing_zip, :cc_number, :cc_exp_month, :cc_exp_year, :cc_sec_code, :billing_zip) + end + + def find_order + @order = Order.find(params[:id]) + end + + + + def paid_order_status + @order.order_status = "paid" + @order.save + end + +end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb new file mode 100644 index 0000000000..4e689589f8 --- /dev/null +++ b/app/controllers/products_controller.rb @@ -0,0 +1,110 @@ +class ProductsController < ApplicationController + before_action :find_product, only: [:edit, :update, :reinstate_product, :retire_product] + before_action :merchant_owns_product, only: [:edit] + + def index + if params[:search] + @products = Product.where(active: true).search(params[:search]).order("created_at DESC") + else + @products = Product.where(active: true).order("created_at DESC") + if current_user + @inactive_products = Product.where(merchant_id: current_user.id).where(active: false) + end + end + end + + def show + + begin + product = Product.find(params[:id]) + if product.active == true || product.merchant_id == current_user.id + @product = product + @reviews = Review.where(product_id: @product.id) + @average = Product.average(@reviews) + else + redirect_to products_path + end + rescue StandardError => err + render "/errors/not_found", status: :not_found + end + @order = current_order + @order_item = @order.order_items.new + end + + def new + @product = Product.new + @categories_array = Category.all.map { |category| [Category.find(category.id).name, category.id] } + @prod_cat = CategoryProduct.new + end + + def create + @product = Product.new(product_params) + @product.merchant_id = session[:merchant_id] + if @product.save + @product.price *= 100 + @product.save + + params[:category_ids].each do |c| + @product.categories << Category.find(c) + end + redirect_to product_path(@product) + else + @categories_array = Category.all.map { |category| [Category.find(category.id).name, category.id] } + render :new + end + end + + def edit; + @product.price /= 100.0 + @categories_array = Category.all.map { |category| [Category.find(category.id).name, category.id] } + end + + def update + @product.price *= 100 + if @product.update(product_params) + @product.categories.each do |i| + @product.categories.delete(Category.find(i.id)) + end + params[:category_ids].each do |c| + @product.categories << Category.find(c) + end + redirect_to product_path + else + render :edit + end + end + + def destroy + @product = Product.find(params[:id]).destroy + redirect_to products_path + end + + def retire_product + @product.active = false + @product.save + redirect_to products_path + end + + def reinstate_product + @product.active = true + @product.save + redirect_to product_path + end + + def merchant_owns_product + product = Product.find(params[:id]) + unless current_user && current_user.id == product.merchant_id + redirect_to product_path(product) + end + end + + private + + def product_params + params.require(:product).permit(:name, :price, :inventory, :description, :photo_url) + end + + def find_product + @product = Product.find(params[:id]) + end +end diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb new file mode 100644 index 0000000000..01878eb7aa --- /dev/null +++ b/app/controllers/reviews_controller.rb @@ -0,0 +1,34 @@ +class ReviewsController < ApplicationController + before_action :merchant_product_self_review, only: [:new] + + def new + @review = Review.new + @review.product_id = params[:product_id] + end + + def create + @review = Review.new(review_params) + @product = Product.find(params[:id]) + @review.product_id = @product.id + if @review.save + redirect_to product_path(@review.product_id) + else + render :new + end + end + + def merchant_product_self_review + product = Product.find(params[:product_id]) + if current_user && current_user.id == product.merchant_id + # flash[:error] = "You cannot review your own product" + redirect_to product_path(product) + end + end + + private + + def review_params + params.require(:review).permit(:rating, :description, :product_id) + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000000..f7e53e0844 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,39 @@ +class SessionsController < ApplicationController +skip_before_action :require_login, only: [:create, :login_failure] + + def index + if session[:merchant_id].nil? + redirect_to root_path + return + else + @merchant = Merchant.find(session[:merchant_id]) # < recalls the value set in a previous request + end + end + + def create + auth_hash = request.env['omniauth.auth'] + redirect_to login_failure_path if auth_hash['uid'].nil? + + @merchant = Merchant.find_by(uid: auth_hash[:uid], provider: 'github') # Remember: .find_by always returns one object (the first that matches) + if @merchant.nil? + # merchant doesn't match anything in the DB. + # Attempt to create a new merchant + @merchant = Merchant.build_from_github(auth_hash) + render :login_failure and return unless @merchant.save # This line saves the newly built @merchant to the database if it can be saved + end + # Save the merchant ID in the session + session[:merchant_id] = @merchant.id + redirect_to root_path + end + + def login_failure; end + + def destroy + if current_user + session.delete(:merchant_id) + session.delete(:order_id) + end + redirect_to root_path + end + +end diff --git a/app/helpers/carts_helper.rb b/app/helpers/carts_helper.rb new file mode 100644 index 0000000000..d99c380cb5 --- /dev/null +++ b/app/helpers/carts_helper.rb @@ -0,0 +1,2 @@ +module CartsHelper +end diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb new file mode 100644 index 0000000000..e06f31554c --- /dev/null +++ b/app/helpers/categories_helper.rb @@ -0,0 +1,2 @@ +module CategoriesHelper +end diff --git a/app/helpers/guests_helper.rb b/app/helpers/guests_helper.rb new file mode 100644 index 0000000000..00317bf2cf --- /dev/null +++ b/app/helpers/guests_helper.rb @@ -0,0 +1,2 @@ +module GuestsHelper +end diff --git a/app/helpers/merchants_helper.rb b/app/helpers/merchants_helper.rb new file mode 100644 index 0000000000..5337747b0f --- /dev/null +++ b/app/helpers/merchants_helper.rb @@ -0,0 +1,2 @@ +module MerchantsHelper +end diff --git a/app/helpers/orderitems_helper.rb b/app/helpers/orderitems_helper.rb new file mode 100644 index 0000000000..c74e407d1c --- /dev/null +++ b/app/helpers/orderitems_helper.rb @@ -0,0 +1,2 @@ +module OrderitemsHelper +end diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb new file mode 100644 index 0000000000..6e7f858045 --- /dev/null +++ b/app/helpers/orders_helper.rb @@ -0,0 +1,23 @@ +module OrdersHelper + + def states + ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', + 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', + 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', + 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', + 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', + 'Pennsylvania', 'Puerto Rico', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', + 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'] + end + + def months + [1,2,3,4,5,6,7,8,9,10,11,12] + end + + def expiration_year + yrs = [] + year_today = Date.today.strftime("%y").to_i + year_today.upto(year_today+10).each do |y| yrs << y.to_s;end + return yrs + end +end diff --git a/app/helpers/products_helper.rb b/app/helpers/products_helper.rb new file mode 100644 index 0000000000..ab5c42b325 --- /dev/null +++ b/app/helpers/products_helper.rb @@ -0,0 +1,2 @@ +module ProductsHelper +end diff --git a/app/helpers/reviews_helper.rb b/app/helpers/reviews_helper.rb new file mode 100644 index 0000000000..682b7b1abc --- /dev/null +++ b/app/helpers/reviews_helper.rb @@ -0,0 +1,2 @@ +module ReviewsHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000000..309f8b2eb3 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/models/category.rb b/app/models/category.rb new file mode 100644 index 0000000000..c841c80eab --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,6 @@ +class Category < ActiveRecord::Base + has_many :category_products + has_many :products, through: :category_products + + validates :name, presence: true, uniqueness: true +end diff --git a/app/models/category_product.rb b/app/models/category_product.rb new file mode 100644 index 0000000000..631d4e952c --- /dev/null +++ b/app/models/category_product.rb @@ -0,0 +1,4 @@ +class CategoryProduct < ActiveRecord::Base + belongs_to :category + belongs_to :product +end diff --git a/app/models/merchant.rb b/app/models/merchant.rb new file mode 100644 index 0000000000..0be60fb72b --- /dev/null +++ b/app/models/merchant.rb @@ -0,0 +1,18 @@ +class Merchant < ActiveRecord::Base + has_many :products + + validates :username, presence: true, uniqueness: true + validates :email, presence: true, uniqueness: true + # make email have format requirements + + def self.build_from_github(auth_hash) # factoring method - creates a new User and populates it with data from the auth_hash + merchant = Merchant.new + merchant.uid = auth_hash[:uid] + merchant.provider = 'github' + merchant.username = auth_hash['info']['name'] + merchant.email = auth_hash['info']['email'] + + return merchant + end + +end diff --git a/app/models/order.rb b/app/models/order.rb new file mode 100644 index 0000000000..388e0c1b2f --- /dev/null +++ b/app/models/order.rb @@ -0,0 +1,44 @@ +class Order < ActiveRecord::Base + has_many :order_items + before_validation :initial_order_status, only: [:create] + + validates :name, presence: true, uniqueness: { scope: :street_address, :message => "There is already a buyer with this name and address. Please enter a different name and/or address." }, unless: :pending? + validates :email, presence: true, uniqueness: true, format: { with: /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]+\z/, + message: "Email address is not in the correct format" }, unless: :pending? + validates :street_address, presence: true, unless: :pending? + validates :city, presence: true, unless: :pending? + validates :state, presence: true, unless: :pending? + validates :mailing_zip, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{5}\z/, + message: "Mailing zip code is invalid, must be 5 digits long" }, unless: :pending? + validates :cc_number, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{15,16}\z/, + message: "Credit card is not valid" }, unless: :pending? + validates :cc_exp_month, presence: true, unless: :pending? + validates :cc_exp_year, presence: true, unless: :pending? + validates :cc_sec_code, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{3,4}\z/, + message: "Credit card security code is not valid" }, unless: :pending? + validates :billing_zip, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{5}\z/, + message: "Billing zip code is invalid, must be 5 digits long" }, unless: :pending? + + # validate :card_not_expired, unless: :pending? # will come back to this + + + private + + # def card_not_expired + # return if cc_exp_month.nil? || cc_exp_year.nil? + # if (cc_exp_month < Time.now.month) && (cc_exp_year <= Time.now.year) || + # errors.add(:cc_exp_year, "Bummer. Your card is expired. Please use a different card.") + # end + # end + + def initial_order_status + if order_status == nil + self.order_status = "pending" + end + end + + def pending? + self.order_status == "pending" + end + +end diff --git a/app/models/order_item.rb b/app/models/order_item.rb new file mode 100644 index 0000000000..79c473b5d6 --- /dev/null +++ b/app/models/order_item.rb @@ -0,0 +1,15 @@ +class OrderItem < ActiveRecord::Base + belongs_to :order + belongs_to :product + + validates :quantity, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1 } + validate :quantity_not_greater_than_inventory + + private + + def quantity_not_greater_than_inventory + if quantity == nil || quantity > Product.find(product_id).inventory + errors.add(:quantity, "Bummer. There are only #{Product.find(product_id).inventory} #{Product.find(product_id).name.pluralize} available.") + end + end +end diff --git a/app/models/product.rb b/app/models/product.rb new file mode 100644 index 0000000000..098baadcb9 --- /dev/null +++ b/app/models/product.rb @@ -0,0 +1,36 @@ +class Product < ActiveRecord::Base + has_many :order_items + has_many :reviews + has_many :category_products + has_many :categories, through: :category_products + belongs_to :merchant + + validates :name, presence: true, uniqueness: true + validates :price, presence: true, numericality: { greater_than: 0 } + validates :inventory, presence: true, numericality: { only_integer: true, greater_than: 0 } + validates :description, presence: true + validates :photo_url, presence: true + + def self.find_photo_image(product) + if product.photo_url == "1" + image = "#{ product.name.downcase.gsub(' ', '_') }.jpeg" + else + image = product.photo_url + end + return image + end + + def self.search(search) + where(("name ILIKE ? OR description ILIKE ?"), "%#{search}%", "%#{search}%") + end + + def self.average(reviews) + ratings = [] + num_ratings = reviews.count + reviews.each do |i| + ratings << i[:rating] + end + average = (ratings.sum)/num_ratings.to_f + return sprintf('%.1f' % average) + end +end diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 0000000000..0c415dd1f2 --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,6 @@ +class Review < ActiveRecord::Base + belongs_to :product + + validates :rating, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } + +end diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb new file mode 100644 index 0000000000..05f987d1c7 --- /dev/null +++ b/app/views/categories/new.html.erb @@ -0,0 +1,17 @@ +<% if @category.errors.any? %> +
<%= link_to "#{ p.name }", product_path(p) %> <%= "$#{ sprintf('%.2f' % (p.price/100.0)) }" %>
+<%= link_to featured_image.name, product_path(featured_image) %> <%= "$#{ sprintf('%.2f' % (featured_image.price/100.0)) }" %>
+<%= link_to "m.name", merchant_path(m) %>
+<% end %> diff --git a/app/views/merchants/new.html.erb b/app/views/merchants/new.html.erb new file mode 100644 index 0000000000..f071cba10a --- /dev/null +++ b/app/views/merchants/new.html.erb @@ -0,0 +1,2 @@ + +<%#= render partial: "form" %> diff --git a/app/views/merchants/products.html.erb b/app/views/merchants/products.html.erb new file mode 100644 index 0000000000..a9d77ad040 --- /dev/null +++ b/app/views/merchants/products.html.erb @@ -0,0 +1,37 @@ +<%= link_to p.name, product_path(p) %> <%= "$#{ sprintf('%.2f' % (p.price/100.0)) }" %>
+ <% if current_user != nil && @merchant.id == current_user.id %> + + <% end %> +
+ <%= i.name %>
+
+ <% image = i.name.downcase.gsub(' ', '_') %>
+
+ <% image = Product.find_photo_image(i) %>
+ <%= link_to image_tag(image, alt: "#{ i.name }", class: "rounded-image"), product_path(i) %>
+
<%= link_to "Add a New Product", new_product_path %>
+<% end %> diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb new file mode 100644 index 0000000000..38732abfb3 --- /dev/null +++ b/app/views/merchants/show.html.erb @@ -0,0 +1,91 @@ +| + Order Number + | ++ Products + | ++ + | +
|---|---|---|
| + Dynamic list of order number with ordered on: information + | ++ Products in that order + | ++ Order status generated by dropdown menu + | +
+ Total Revenue for Pending Orders: +
++ Total Revenue for Paid Orders: +
++ Total Revenue for Cancelled Orders: +
++ Total Revenue for Completed Orders: +
++ Total Net Revenue: +
++ Total Pending Orders: +
++ Total Cancelled Orders: +
++ Total Paid Orders: +
++ Total Completed Orders: +
++ Total Orders: +
+<%= "Unit Price: $#{ sprintf('%.2f' % (product.price / 100.0)) }" %>
+ <%= f.number_field :quantity, value: order_item.quantity.to_i, min: 1 %> + <%= f.hidden_field :product_id, value: product.id %> + + <%= f.submit "Update Quantity", class: "btn btn-primary" %> + <%= link_to "Remove item from cart", order_item, { data: { confirm: "Are you sure you wish to delete the product '#{order_item.product.name}' from your cart?" }, method: :delete, remote: true, class: "btn btn-danger" } %> +<% end %> diff --git a/app/views/order_items/index.html.erb b/app/views/order_items/index.html.erb new file mode 100644 index 0000000000..30a6560613 --- /dev/null +++ b/app/views/order_items/index.html.erb @@ -0,0 +1,15 @@ +There are no items in your shopping cart. Please <%= link_to "go back", root_path %> and add some items to your cart.
+<% else %> + <% @order_items.each do |order_item| %> + <%= render 'order_items/shopping_cart_items', product: order_item.product, order_item: order_item, show_total: true %> + <% product_total_price = (order_item.product.price * order_item.quantity / 100.0).to_f %> +<%= "Total Price: $#{ sprintf('%.2f' % product_total_price) }" %>
+ <% grand_total += product_total_price %> + <% end %> +<%= "Cart total: $#{ sprintf('%.2f' % grand_total) }" %>
+ <%= link_to "Checkout", edit_order_path(@order), class: "button" %> +<% end %> diff --git a/app/views/orders/_form.html.erb b/app/views/orders/_form.html.erb new file mode 100644 index 0000000000..0571f2636f --- /dev/null +++ b/app/views/orders/_form.html.erb @@ -0,0 +1,49 @@ +<% if @order.errors.any? %> +alert("not valid.") +<%= p.name %> <%= "$#{ sprintf('%.2f' % (p.price/100.0)) }" %>
+<%= i.name %> <%= "$#{ sprintf('%.2f' % (i.price/100.0)) }" %>
+<%= image_tag image, alt: "#{ @product.name }" %>
+<%= "Description: #{ @product.description }" %>
+<%= "Price: $#{ sprintf('%.2f' % (@product.price/100.0)) }" %>
+ <%= form_for @order_item, method: :post do |f| %> + <%= f.number_field :quantity, value: 1, min: 1 %> + + <%= f.hidden_field :product_id, value: @product.id %> +Stock: <%= @product.inventory %>
+<% r.rating.times do |i| %> + <%= "🐵 " %> + <% end %> + <%= ": #{ r.description }" %>
+ <% end %> + <% else %> +<%= link_to "Review this product", new_product_review_path(@product) %>
+ <% else %> +You cannot review your own product
+ <% end %> + + + diff --git a/app/views/reviews/_form.html.erb b/app/views/reviews/_form.html.erb new file mode 100644 index 0000000000..12360aaed0 --- /dev/null +++ b/app/views/reviews/_form.html.erb @@ -0,0 +1,23 @@ +<% if @review.errors.any? %> +