diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000..1ad899dbcd Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..134ae2232c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore all logfiles and tempfiles. +/coverage/* +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore uploaded files in development +/storage/* +!/storage/.keep + +/node_modules +/yarn-error.log + +/public/assets +.byebug_history + + +.env +# Ignore master key for decrypting credentials and more. +/config/master.key + + +* Ignore .env file +/.env diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000000..25c81fe399 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-2.5.1 \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..8b55d00ef8 --- /dev/null +++ b/Gemfile @@ -0,0 +1,93 @@ +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +ruby '2.5.1' + +# goeun's machine needs this version of autoprefixer +gem 'autoprefixer-rails', '8.6.5' + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.2.1' +# Use postgresql as the database for Active Record +gem 'pg', '>= 0.18', '< 2.0' +# Use Puma as the app server +gem 'puma', '~> 3.11' +# Use SCSS for stylesheets +gem 'sass-rails', '~> 5.0' +# Use Uglifier as compressor for JavaScript assets +gem 'uglifier', '>= 1.3.0' +gem 'omniauth' + +gem 'omniauth-github' + +# See https://github.com/rails/execjs#readme for more supported runtimes +# gem 'mini_racer', platforms: :ruby + +# Use CoffeeScript for .coffee assets and views +# gem 'coffee-rails', '~> 4.2' +# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks +gem 'turbolinks', '~> 5' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.5' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 4.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use ActiveStorage variant +# gem 'mini_magick', '~> 4.8' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +# Reduces boot times through caching; required in config/boot.rb +gem 'bootsnap', '>= 1.1.0', require: false + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] +end + +group :development do + # Access an interactive console on exception pages or by calling 'console' anywhere in the code. + gem 'web-console', '>= 3.3.0' + gem 'listen', '>= 3.0.5', '< 3.2' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' +end + +group :test do + # Adds support for Capybara system testing and selenium driver + gem 'capybara', '>= 2.15' + gem 'selenium-webdriver' + # Easy installation and use of chromedriver to run system tests with Chrome + gem 'chromedriver-helper' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +gem 'jquery-rails' +gem 'jquery-turbolinks' +gem 'bootstrap', '~> 4.1.3' +group :development, :test do + gem 'pry-rails' +end + +group :development do + gem 'dotenv-rails' + gem 'better_errors' + gem 'binding_of_caller' + gem 'guard' + gem 'guard-minitest' +end + +group :test do + gem 'simplecov', require: false + gem 'minitest-rails' + gem 'minitest-reporters' + gem 'simplecov', require: false +end + +gem 'will_paginate', "~> 3.0.4" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..08224e6d61 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,316 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.2.1) + actionpack (= 5.2.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.1) + actionpack (= 5.2.1) + actionview (= 5.2.1) + activejob (= 5.2.1) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.2.1) + actionview (= 5.2.1) + activesupport (= 5.2.1) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.2.1) + activesupport (= 5.2.1) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.2.1) + activesupport (= 5.2.1) + globalid (>= 0.3.6) + activemodel (5.2.1) + activesupport (= 5.2.1) + activerecord (5.2.1) + activemodel (= 5.2.1) + activesupport (= 5.2.1) + arel (>= 9.0) + activestorage (5.2.1) + actionpack (= 5.2.1) + activerecord (= 5.2.1) + marcel (~> 0.3.1) + activesupport (5.2.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + ansi (1.5.0) + archive-zip (0.11.0) + io-like (~> 0.3.0) + arel (9.0.0) + autoprefixer-rails (8.6.5) + execjs + better_errors (2.5.0) + coderay (>= 1.0.0) + erubi (>= 1.0.0) + rack (>= 0.9.0) + bindex (0.5.0) + binding_of_caller (0.8.0) + debug_inspector (>= 0.0.1) + bootsnap (1.3.2) + msgpack (~> 1.0) + bootstrap (4.1.3) + autoprefixer-rails (>= 6.0.3) + popper_js (>= 1.12.9, < 2) + sass (>= 3.5.2) + builder (3.2.3) + byebug (10.0.2) + capybara (3.9.0) + addressable + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + xpath (~> 3.1) + childprocess (0.9.0) + ffi (~> 1.0, >= 1.0.11) + chromedriver-helper (2.1.0) + archive-zip (~> 0.10) + nokogiri (~> 1.8) + coderay (1.1.2) + concurrent-ruby (1.0.5) + crass (1.0.4) + debug_inspector (0.0.3) + docile (1.3.1) + dotenv (2.5.0) + dotenv-rails (2.5.0) + dotenv (= 2.5.0) + railties (>= 3.2, < 6.0) + erubi (1.7.1) + execjs (2.7.0) + faraday (0.15.3) + multipart-post (>= 1.2, < 3) + ffi (1.9.25) + formatador (0.2.5) + globalid (0.4.1) + activesupport (>= 4.2.0) + guard (2.14.2) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (>= 1.0.12, < 2.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-minitest (2.4.6) + guard-compat (~> 1.2) + minitest (>= 3.0) + hashie (3.5.7) + i18n (1.1.1) + concurrent-ruby (~> 1.0) + io-like (0.3.0) + jbuilder (2.7.0) + activesupport (>= 4.2.0) + multi_json (>= 1.2) + jquery-rails (4.3.3) + 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 (2.1.0) + jwt (2.1.0) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + loofah (2.2.2) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + lumberjack (1.0.13) + mail (2.7.1) + mini_mime (>= 0.1.1) + marcel (0.3.3) + mimemagic (~> 0.3.2) + method_source (0.9.0) + mimemagic (0.3.2) + mini_mime (1.0.1) + mini_portile2 (2.3.0) + minitest (5.11.3) + minitest-rails (3.0.0) + minitest (~> 5.8) + railties (~> 5.0) + minitest-reporters (1.3.5) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + msgpack (1.2.4) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nenv (0.3.0) + nio4r (2.3.1) + nokogiri (1.8.5) + mini_portile2 (~> 2.3.0) + notiffany (0.1.1) + nenv (~> 0.1) + shellany (~> 0.0) + oauth2 (1.4.1) + faraday (>= 0.8, < 0.16.0) + jwt (>= 1.0, < 3.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.8.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) + omniauth-github (1.3.0) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) + omniauth-oauth2 (1.5.0) + oauth2 (~> 1.1) + omniauth (~> 1.2) + pg (1.1.3) + popper_js (1.14.3) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry-rails (0.3.6) + pry (>= 0.10.4) + public_suffix (3.0.3) + puma (3.12.0) + rack (2.0.5) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.2.1) + actioncable (= 5.2.1) + actionmailer (= 5.2.1) + actionpack (= 5.2.1) + actionview (= 5.2.1) + activejob (= 5.2.1) + activemodel (= 5.2.1) + activerecord (= 5.2.1) + activestorage (= 5.2.1) + activesupport (= 5.2.1) + bundler (>= 1.3.0) + railties (= 5.2.1) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + railties (5.2.1) + actionpack (= 5.2.1) + activesupport (= 5.2.1) + method_source + rake (>= 0.8.7) + thor (>= 0.19.0, < 2.0) + rake (12.3.1) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + ruby-progressbar (1.10.0) + ruby_dep (1.5.0) + rubyzip (1.2.2) + sass (3.6.0) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + selenium-webdriver (3.14.1) + childprocess (~> 0.5) + rubyzip (~> 1.2, >= 1.2.2) + shellany (0.0.1) + simplecov (0.16.1) + docile (~> 1.1) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.2) + spring (2.0.2) + activesupport (>= 4.2) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) + sprockets (3.7.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + thor (0.20.0) + thread_safe (0.3.6) + tilt (2.0.8) + turbolinks (5.2.0) + turbolinks-source (~> 5.2) + turbolinks-source (5.2.0) + tzinfo (1.2.5) + thread_safe (~> 0.1) + uglifier (4.1.19) + execjs (>= 0.3.0, < 3) + web-console (3.7.0) + actionview (>= 5.0) + activemodel (>= 5.0) + bindex (>= 0.4.0) + railties (>= 5.0) + websocket-driver (0.7.0) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.3) + will_paginate (3.0.12) + xpath (3.2.0) + nokogiri (~> 1.8) + +PLATFORMS + ruby + +DEPENDENCIES + autoprefixer-rails (= 8.6.5) + better_errors + binding_of_caller + bootsnap (>= 1.1.0) + bootstrap (~> 4.1.3) + byebug + capybara (>= 2.15) + chromedriver-helper + dotenv-rails + guard + guard-minitest + jbuilder (~> 2.5) + jquery-rails + jquery-turbolinks + listen (>= 3.0.5, < 3.2) + minitest-rails + minitest-reporters + omniauth + omniauth-github + pg (>= 0.18, < 2.0) + pry-rails + puma (~> 3.11) + rails (~> 5.2.1) + sass-rails (~> 5.0) + selenium-webdriver + simplecov + spring + spring-watcher-listen (~> 2.0.0) + turbolinks (~> 5) + tzinfo-data + uglifier (>= 1.3.0) + web-console (>= 3.3.0) + will_paginate (~> 3.0.4) + +RUBY VERSION + ruby 2.5.1p57 + +BUNDLED WITH + 1.16.4 diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000000..e34f706f4a --- /dev/null +++ b/Guardfile @@ -0,0 +1,9 @@ +guard :minitest, autorun: false, spring: true do + watch(%r{^app/(.+).rb$}) { |m| "test/#{m[1]}_test.rb" } + watch(%r{^app/controllers/application_controller.rb$}) { 'test/controllers' } + watch(%r{^app/controllers/(.+)_controller.rb$}) { |m| "test/integration/#{m[1]}_test.rb" } + watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" } + watch(%r{^lib/(.+).rb$}) { |m| "test/lib/#{m[1]}_test.rb" } + watch(%r{^test/.+_test.rb$}) + watch(%r{^test/test_helper.rb$}) { 'test' } +end diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000000..e85f913914 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/app/.DS_Store b/app/.DS_Store new file mode 100644 index 0000000000..dd6a74021e Binary files /dev/null and b/app/.DS_Store differ diff --git a/app/assets/.DS_Store b/app/assets/.DS_Store new file mode 100644 index 0000000000..acda24e21f Binary files /dev/null and b/app/assets/.DS_Store differ diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000000..b16e53d6d5 --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css diff --git a/app/assets/images/.DS_Store b/app/assets/images/.DS_Store new file mode 100644 index 0000000000..d467085c9f Binary files /dev/null and b/app/assets/images/.DS_Store differ diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/images/Otrio.jpeg b/app/assets/images/Otrio.jpeg new file mode 100644 index 0000000000..a5ad8df039 Binary files /dev/null and b/app/assets/images/Otrio.jpeg differ diff --git a/app/assets/images/cart.png b/app/assets/images/cart.png new file mode 100644 index 0000000000..4353ec445b Binary files /dev/null and b/app/assets/images/cart.png differ diff --git a/app/assets/images/drink_tac_toe.jpg b/app/assets/images/drink_tac_toe.jpg new file mode 100644 index 0000000000..bc5772eaf3 Binary files /dev/null and b/app/assets/images/drink_tac_toe.jpg differ diff --git a/app/assets/images/image_not_available.jpg b/app/assets/images/image_not_available.jpg new file mode 100644 index 0000000000..f6ff41496b Binary files /dev/null and b/app/assets/images/image_not_available.jpg differ diff --git a/app/assets/images/kanoodle.jpg b/app/assets/images/kanoodle.jpg new file mode 100644 index 0000000000..3752c0649b Binary files /dev/null and b/app/assets/images/kanoodle.jpg differ diff --git a/app/assets/images/monopoly.jpg b/app/assets/images/monopoly.jpg new file mode 100644 index 0000000000..6237595213 Binary files /dev/null and b/app/assets/images/monopoly.jpg differ diff --git a/app/assets/images/sherlock-game.jpg b/app/assets/images/sherlock-game.jpg new file mode 100644 index 0000000000..97b9150b37 Binary files /dev/null and b/app/assets/images/sherlock-game.jpg differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 0000000000..4f73c21a7d --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,20 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's +// vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. + //= require jquery3 + //= require popper + //= require bootstrap-sprockets + +// +//= require rails-ujs +//= require activestorage +//= require turbolinks +//= require_tree . diff --git a/app/assets/javascripts/cable.js b/app/assets/javascripts/cable.js new file mode 100644 index 0000000000..739aa5f022 --- /dev/null +++ b/app/assets/javascripts/cable.js @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/app/assets/javascripts/categories.js b/app/assets/javascripts/categories.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/categories.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/channels/.keep b/app/assets/javascripts/channels/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/javascripts/merchants.js b/app/assets/javascripts/merchants.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/merchants.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/order_items.js b/app/assets/javascripts/order_items.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/order_items.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/orders.js b/app/assets/javascripts/orders.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/orders.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/products.js b/app/assets/javascripts/products.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/products.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/reviews.js b/app/assets/javascripts/reviews.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/reviews.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/sessions.js b/app/assets/javascripts/sessions.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/sessions.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 0000000000..6f453101e6 --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,133 @@ +@import "bootstrap"; +/* Import scss content */ +@import "**/*"; + +// SITE WIDE +*{ + font-family: 'Montserrat', sans-serif; +} + +a{ + color: rgb(205, 205, 205); +} + +a:hover{ + color: rgb(124, 121, 121); + text-decoration: none; + font-weight: bold; +} + +button a, a:hover{ + color: white; +} + +// main{ +// padding-left: 5%; +// padding-right: 5%; +// } + + +// NAVIGATION BAR +header nav { + display: grid; + grid-column: 35% 15% 15% 15% auto; + grid-rows: 20px auto; + padding: 20px; + grid-gap: 10px; + background-color: rgb(42, 42, 42); + color: rgb(223, 223, 223); +} + +.braineaze{ + grid-column: 1 / 2; + grid-row: 2 / 3; +} + +.braineaze h1{ + text-transform: uppercase; + background: linear-gradient(to right, #30CFD0 0%, #ff291d 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.hello{ + grid-column: 1 / 2; + grid-row: 1 / 2; +} + +.login{ + grid-column: 2 / 6; + grid-row: 1 / 2; + text-align: right; +} + +.login #my_account{ + margin-right: 20px; +} + +.nav-option{ + display: flex; + align-items: center; + text-transform: uppercase; +} + +#products{ + grid-column: 2 / 3; + grid-row: 2 / 3; +} + + +#category{ + grid-column: 3 / 4; + grid-row: 2 / 3; +} + +.dropdown-menu{ + background-color: rgb(42, 42, 42); + border: none; + padding: 15px; +} + +.dropdown-menu li{ + padding-top: 10px; +} + + +#merchants{ + grid-column: 4 / 5; + grid-row: 2 / 3; +} + +#cart{ + display: flex; + justify-content: center; + align-items: center; + grid-column: 5 / 6; + grid-row: 2 / 3; +} + +#cart .cart-photo{ + width: 40px; + height:40px; +} + +// BODY + +main{ + padding-top: 30px; + padding-bottom: 100px; + background-color: rgb(252, 249, 244); +} + +// FOOTER + +footer{ + display: flex; + flex-direction: column; + width: 100%; + padding: 20px; + justify-content: center; + align-items: center; + background-color: rgb(38, 38, 38); + color: rgb(223, 223, 223); +} 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/merchants.scss b/app/assets/stylesheets/merchants.scss new file mode 100644 index 0000000000..de3828f47d --- /dev/null +++ b/app/assets/stylesheets/merchants.scss @@ -0,0 +1,30 @@ +// 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/ + +.tags { + text-align: center; +} + +.merchant-detail-summary { +} + +.general{ + display: flex; + flex-direction: row; + justify-content: space-around; + margin-top: 20px; +} + +.general button{ + margin: 20px; + padding: 10px; +} +.orders-merchandise{ + margin-left: 8%; + margin-right: 8%; +} + +.orders-merchandise h3{ + padding-top: 50px; +} diff --git a/app/assets/stylesheets/order_items.scss b/app/assets/stylesheets/order_items.scss new file mode 100644 index 0000000000..c02e2068ea --- /dev/null +++ b/app/assets/stylesheets/order_items.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the order_items 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..5b912f9466 --- /dev/null +++ b/app/assets/stylesheets/products.scss @@ -0,0 +1,126 @@ +// 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/ + +#home-container { + margin: 0; + padding: 5%; + margin-top: 50px; + background-color: rgb(226, 224, 218); +} + +.flex-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; + align-content: space-between; +} + +.card { + margin-top: 2%; + margin-bottom: 3%; +} + +.image-tag { + width: 300px; + height: 200px; +} + +.title-page { + padding-bottom: 100px; + color: rgb(61, 61, 61); + text-align: center; + text-transform: uppercase; +} + +.product-info { + text-align: center; +} + +.product-thumbnail { + margin-top: 50px; + border: 1px solid green; +} + +.page-container { + border: 1px solid black; +} + +.image-show-tag { + padding-bottom: 8%; + width: 500px; + height: 400px; +} + +.pagination { + .previous_page { background:#ffffff; color:white; padding: .5em; font-size: .8em; color: #35324d;} + .previous_page.disabled { background: white; color: #dedede; border: 2px solid #dedede;} + .current { background: #30CFD0; color: #ffffff; border: .4em solid #30CFD0;} + .next_page { background:#ffffff; color:white; padding: .5em; font-size: .8em; color: #35324d;} + .next_page.disabled { background: white; color: #dedede; border: 2px solid #dedede;} + padding-top: 20px; + display: flex; + flex-direction: row; + justify-content: center; +} + +.pagination > *{ + margin-left: 10px; + margin-right: 10px; + +} + +.blockquote { + font-style: italic; +} + +.blockquote p:before { + content: open-quote; +} + +.blockquote p:after { + content: close-quote; +} + +.form-checkboxes{ + height: 150px; + display: flex; + flex-direction: column; + flex-wrap: wrap; +} + +.label-checkbox{ + margin-left: 100px; +} + +.quote:before { + content: open-quote; +} + +.quote:after { + content: close-quote; +} + +#home-banner { + padding-top: 200px; +} + +.text-info { + margin-top: 5%; + height: 200px; + font-size: 32px; + text-align: center; +} + +.btn-link{ + text-decoration: none; + color: rgb(88, 88, 88); + +} + +.btn-link:hover{ + text-decoration: none; + font-style: italic; + color: rgb(32, 32, 32); +} diff --git a/app/assets/stylesheets/reviews.scss b/app/assets/stylesheets/reviews.scss new file mode 100644 index 0000000000..8ceaf128aa --- /dev/null +++ b/app/assets/stylesheets/reviews.scss @@ -0,0 +1,73 @@ +// 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/ + +.rating { + display: inline-block; + position: relative; + height: 50px; + line-height: 50px; + font-size: 50px; +} + +.rating label { + position: absolute; + top: 0; + left: 0; + height: 100%; + cursor: pointer; +} + +.rating label:last-child { + position: static; +} + +.rating label:nth-child(1) { + z-index: 5; +} + +.rating label:nth-child(2) { + z-index: 4; +} + +.rating label:nth-child(3) { + z-index: 3; +} + +.rating label:nth-child(4) { + z-index: 2; +} + +.rating label:nth-child(5) { + z-index: 1; +} + +.rating label input { + position: absolute; + top: 0; + left: 0; + opacity: 0; +} + +.rating label .icon { + float: left; + color: transparent; +} + +.rating label:last-child .icon { + color: #000; +} + +.rating:not(:hover) label input:checked ~ .icon, +.rating:hover label:hover input ~ .icon { + color: #09f; +} + +.rating label input:focus:not(:checked) ~ .icon:last-child { + color: #000; + text-shadow: 0 0 5px #09f; +} + +.textbox { + +} diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 0000000000..ccb1ed25b2 --- /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/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000000..d672697283 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 0000000000..0ff5442f47 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000000..fa709db1fd --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,62 @@ +class ApplicationController < ActionController::Base + before_action :current_merchant + before_action :current_cart + before_action :require_login + + private + + def current_merchant + @current_merchant = Merchant.find_by(id: session[:merchant_id]) + end + + def require_login + if @current_merchant.nil? + flash[:error] = "You must be logged in to view this section" + redirect_to root_path + end + end + + def current_cart + existing_cart = session[:order_id] + existing_cart ? find_shopping_cart : create_shopping_cart + end + + def find_shopping_cart + @shopping_cart = Order.find_by(id: session[:order_id]) + return @shopping_cart + end + + def create_shopping_cart + @shopping_cart = Order.create! + session[:order_id] = @shopping_cart.id + return @shopping_cart + end + + def find_merchant + return Merchant.find_by(id: params[:merchant_id]) + end + + def find_category + return Category.find_by(id: params[:category_id]) + end + + def find_order_item + return OrderItem.find_by(id: params[:id]) + end + + def find_product + return Product.find_by(id: params[:id]) + end + + def find_order + return Order.find_by(id: params[:id]) + end + + def render_404 + render file: "/public/404.html", status: 404 + end + + def render_400 + render :new, status: :bad_request + end +end diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000000..3970a72f72 --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,27 @@ +class CategoriesController < ApplicationController + + def new + # puts params.to_h + @category = Category.new + end + + def create + @category = Category.new(category_params) + + if @category.save + flash[:status] = :success + flash[:result_text] = "Successfully created new category: #{@category.name}" + redirect_to categories_path(@category) + else + flash[:status] = :failure + flash[:result_text] = "Could not create #{@category.name}" + flash[:messages] = @category.errors.messages + render :new, status: :bad_request + end + end + + private + def category_params + params.require(:category).permit(:name) + end +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb new file mode 100644 index 0000000000..349a9644e7 --- /dev/null +++ b/app/controllers/merchants_controller.rb @@ -0,0 +1,16 @@ +class MerchantsController < ApplicationController + + def show + @products = @current_merchant.products + @order_items = [] + @products.each do |product| + all_items = OrderItem.where( product_id: product.id ) + all_items.each do |order_item| + if order_item.status != 'pending' + @order_items << order_item + end + end + 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..db376d0c3c --- /dev/null +++ b/app/controllers/order_items_controller.rb @@ -0,0 +1,142 @@ +class OrderItemsController < ApplicationController + skip_before_action :require_login + + def create + product_id = params[:order_item][:product_id].to_i + product = Product.find_by(id: product_id) + + if @current_merchant == product.merchant + flash[:error] = "Cannot add your own product to the cart" + redirect_to dashboard_path + return + end + + @shopping_cart.order_items.each do |order_item| + if order_item.product.id == product_id + old_qty = order_item.quantity.to_i + requested_qty = params[:order_item][:quantity].to_i + new_qty = old_qty + requested_qty + + if order_item.update(quantity: new_qty) + flash[:success] = "Successfully updated quantity" + redirect_to cart_path + end + return + end + end + + unless quantity_is_in_stock?(product_id) + cannot_order_more_than_stock + return + end + + @order_item = @shopping_cart.order_items.new(order_item_params) + successful_save = @order_item.save + successful_save ? added_to_cart : could_not_add_to_cart + end + + def update + @order_item = find_order_item + product_id = @order_item.product_id + + unless pending_status? + cannot_update_paid_order_item + return + end + + unless quantity_is_in_stock?(product_id) + cannot_order_more_than_stock + return + end + + @order_item.update(order_item_params) + successful_update = @order_item.save + successful_update ? order_item_updated : could_not_update + end + + def destroy + @order_item = find_order_item + pending_status? ? order_item_destroyed : could_not_destroy + end + + def update_order_item_status + valid_statuses = ["shipped", "cancelled"] + order_item_status = params[:status] + @order_item = find_order_item + + if @current_merchant != @order_item.merchant + flash[:alert] = "You do not have access to change the status of this order item" + end + + if @order_item && valid_statuses.include?(order_item_status) && @order_item.merchant == @current_merchant + @order_item.update(status: order_item_status) + flash[:success] = "Successfully changed that order status" + else + flash[:alert] = "Something about that request doesn't exist" + end + redirect_to dashboard_path + end + + private + + def cannot_update_paid_order_item + flash[:error] = "Cannot update an order item that is not pending" + redirect_to root_path + end + + def pending_status? + return @order_item.status == 'pending' + end + + def could_not_destroy + flash[:failure] = "Could not delete item from cart due to status being \'#{@order_item.status}\'" + redirect_to root_path + end + + def order_item_destroyed + @order_item.destroy + flash[:success] = "Successfully deleted item from cart" + redirect_to cart_path + end + + def could_not_update + flash[:error] = @order_item.errors.messages + redirect_to root_path + end + + def order_item_updated + flash[:success] = "Successfully updated item" + redirect_to cart_path + end + + def could_not_add_to_cart + flash[:failure] = "Could not add to cart" + flash[:messages] = @order_item.errors.messages + redirect_to root_path + end + + def added_to_cart + flash[:success] = "Successfully added to cart" + redirect_to cart_path + end + + def cannot_order_more_than_stock + flash[:error] = "Cannot order more than inventory" + redirect_to root_path + end + + def quantity_is_in_stock?(product_id) + product = find_order_item_product(product_id) + quantity = params[:order_item][:quantity].to_i + inventory = product.inventory + return quantity <= inventory + end + + def find_order_item_product(product_id) + return Product.find_by(id: product_id) + end + + def order_item_params + params.require(:order_item).permit(:quantity, :product_id) + end +end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb new file mode 100644 index 0000000000..2ba50137b0 --- /dev/null +++ b/app/controllers/orders_controller.rb @@ -0,0 +1,84 @@ +class OrdersController < ApplicationController + skip_before_action :require_login + + def index + if cart_has_no_items? + flash.now[:alert] = "Your cart is empty" + end + end + + def show + + @order = find_order + + render_404 if @order.nil? + end + + def edit #checkout form + if cart_has_no_items? + redirect_no_items_in_cart + return + else + return @shopping_cart + end + end + + def update #when all billing info is submitted correctly + @order = find_order + if valid_shopping_cart? + @order.update(status: "in progress") + + + if @order.update(billing_params) + @order.order_items.each do |order_item| + order_item.update(status: "paid") + product = Product.find(order_item.product_id) + product.change_inventory(order_item.quantity) + end + + @order.update(status: "paid") + create_shopping_cart + redirect_to order_path(@order.id) + else + flash[:messages] = @order.errors.messages + redirect_to checkout_path + end + + + end + end + + def destroy #clear cart + @order = find_order + render_404 if @order.nil? + @order.order_items.destroy_all + if @order.order_items.empty? + emptied_cart_200 + end + end + + private + + def emptied_cart_200 + flash[:success] = "Successfully emptied your Cart" + redirect_to cart_path + end + + def redirect_no_items_in_cart + flash[:error] = "There are no items to checkout" + redirect_to root_path + return + end + + def cart_has_no_items? + return @shopping_cart.order_items.empty? + end + + def valid_shopping_cart? + return !@shopping_cart.nil? && @shopping_cart.status == "cart" + end + + def billing_params + params.require(:order).permit(:email, :mailing_address, :cc, :cc_name, :cc_expiration, :cvv, :zip) + end +end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb new file mode 100644 index 0000000000..c8187cd7dc --- /dev/null +++ b/app/controllers/products_controller.rb @@ -0,0 +1,101 @@ +class ProductsController < ApplicationController + skip_before_action :require_login, only: [:index, :show, :root] + + def root + end + + def index + merchant_params = params[:merchant_id] + category_params = params[:category_id] + if merchant_params + @merchant = find_merchant + @products = @merchant.products.where(is_active: true) + elsif category_params + @category = find_category + @products = @category.products.where(is_active: true) + else + @products = Product.where(is_active: true).paginate(page: params[:page], per_page: 6) + + @shopping_cart = find_shopping_cart + end + end + + def show + @product = find_product + render_404 if @product.nil? + end + + def new + @product = @current_merchant.products.new + end + + def create + @product = @current_merchant.products.new(product_params) + is_successful_save = @product.save + is_successful_save ? product_created : cannot_create_product + end + + def edit + @product = find_product + render_404 if @product.nil? + end + + def update + @product = find_product + is_successful_update = @product.update(product_params) + is_successful_update ? updated_product : cannot_update_product + end + + def change_status + @product = find_product + + @product.is_active = !@product.is_active + + if @product.save + flash[:success] = "Successfully changed the status of the #{@product.name}" + else + flash[:error] = "Error accured when updating merchandise" + end + + redirect_to dashboard_path + end + + + private + + def cannot_update_product + flash.now[:error] = "Invalid product data" + render(:edit, status: :bad_request) + end + + def updated_product + flash[:success] = "Successfully updated product \"#{@product.name}\"" + redirect_to product_path(@product.id) + end + + def cannot_create_product + flash.now[:error] = "Invalid product data" + + flash[:error] = @product.errors.messages + + render :new, status: :bad_request + end + + def product_created + flash[:success] = "Successfully created new product with title \"#{@product.name}\"" + redirect_to product_path(@product.id) + end + + def product_params + return params.require(:product).permit( + :name, + :price, + :inventory, + :merchant_id, + :description, + :is_active, + :image, + category_ids: [], + ) + end +end diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb new file mode 100644 index 0000000000..5876cb43c5 --- /dev/null +++ b/app/controllers/reviews_controller.rb @@ -0,0 +1,40 @@ +class ReviewsController < ApplicationController + skip_before_action :require_login + + def new + @product = Product.find_by(id: params[:product_id]) + if @product + @review = @product.reviews.new + else + render_404 + end + end + + + def create + product = Product.find_by(id: params[:product_id]) + + if @current_merchant == product.merchant + flash[:error] = "You cannot submit a review for your own product: #{product.name}" + + redirect_to dashboard_path + return + else + @review = product.reviews.new(review_params) + if @review.save + flash[:success] = "Successfully submitted review for: #{product.name}" + redirect_to product_path(product) + else + flash[:error] = "Your review for product #{product.name} was not successfully submitted" + # flash[:messages] = @review.errors.messages + render :new, status: :bad_request + end + end + end + + private + + def review_params + params.require(:review).permit(:rating, :product_id, :description) + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000000..5ba4fd0645 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,43 @@ +class SessionsController < ApplicationController + skip_before_action :require_login, only: [:create] + + def create + auth_hash = request.env['omniauth.auth'] + @merchant = Merchant.find_by(uid: auth_hash[:uid], provider: auth_hash[:provider]) + + if @merchant + # User was found in the database + flash[:success] = "Logged in as returning user #{@merchant.username}" + + else + # User doesn't match anything in the DB + # Attempt to create a new user + + @merchant = Merchant.build_from_github(auth_hash) + successful_save = @merchant.save + + + if successful_save + flash[:success] = "Logged in Successfully. Welcome #{@merchant.username}" + else + # Couldn't save the user for some reason. If we + # hit this it probably means there's a bug with the + # way we've configured GitHub. Our strategy will + # be to display error messages to make future + # debugging easier. + flash[:error] = "Could not create new account: #{@merchant.errors.messages}" + redirect_to root_path + return + end + end + session[:merchant_id] = @merchant.id + redirect_to root_path + end + + def logout + session[:merchant_id] = nil + flash[:success] = "Successfully logged out" + redirect_to root_path + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000000..de6be7945c --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +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/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/order_items_helper.rb b/app/helpers/order_items_helper.rb new file mode 100644 index 0000000000..e197528ae1 --- /dev/null +++ b/app/helpers/order_items_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..443227fd48 --- /dev/null +++ b/app/helpers/orders_helper.rb @@ -0,0 +1,2 @@ +module OrdersHelper +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/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 0000000000..a009ace51c --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000000..286b2239d1 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 0000000000..10a4cba84d --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/category.rb b/app/models/category.rb new file mode 100644 index 0000000000..ca3cae6f23 --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,7 @@ +class Category < ApplicationRecord + has_and_belongs_to_many :products + + validates_associated :products + + validates :name, presence: true, uniqueness: true +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/models/merchant.rb b/app/models/merchant.rb new file mode 100644 index 0000000000..0428d18912 --- /dev/null +++ b/app/models/merchant.rb @@ -0,0 +1,26 @@ +class Merchant < ApplicationRecord + has_many :products + has_many :categories + has_many :order_items, through: :product + validates :username, presence: true, uniqueness: true + validates :email, presence: true, uniqueness: true + + def self.build_from_github(auth_hash) + return Merchant.new(provider: auth_hash[:provider], uid: auth_hash[:uid], email: auth_hash[:info][:email], username: auth_hash[:info][:nickname]) + end + + + def revenue + revenue = 0.0 + self.products.each do |product| + product.order_items.each do |order_item| + if order_item.status != 'pending' + revenue += (order_item.product.price * order_item.quantity) + end + end + end + + return revenue.round(2) + end + +end diff --git a/app/models/order.rb b/app/models/order.rb new file mode 100644 index 0000000000..cf3b955993 --- /dev/null +++ b/app/models/order.rb @@ -0,0 +1,18 @@ +class Order < ApplicationRecord + has_many :order_items + + valid_status = %w(cart paid complete) + + validates :status, presence: true + validates :email, presence: true, unless: :is_cart? + validates :mailing_address, presence: true, unless: :is_cart? + validates :cc, presence: true, unless: :is_cart? + validates :cc_name, presence: true, unless: :is_cart? + validates :cc_expiration, presence: true, unless: :is_cart? + validates :cvv, presence: true, unless: :is_cart? + validates :zip, presence: true, unless: :is_cart? + + def is_cart? + return self.status == 'cart' + end +end diff --git a/app/models/order_item.rb b/app/models/order_item.rb new file mode 100644 index 0000000000..3cdc64b0bc --- /dev/null +++ b/app/models/order_item.rb @@ -0,0 +1,10 @@ +class OrderItem < ApplicationRecord + belongs_to :product + belongs_to :order + has_one :merchant, through: :product + + validates_associated :product, :order, :merchant + validates :quantity, presence: true, :numericality => { only_integer: true, greater_than_or_equal_to: 1 } + validates :status, presence: true + +end diff --git a/app/models/product.rb b/app/models/product.rb new file mode 100644 index 0000000000..547494d304 --- /dev/null +++ b/app/models/product.rb @@ -0,0 +1,35 @@ +class Product < ApplicationRecord + belongs_to :merchant + has_and_belongs_to_many :categories + has_many :order_items, dependent: :destroy + has_many :reviews, dependent: :destroy + + validates :name, presence: true, uniqueness: true + validates :price, presence: true, format: { with: /\A\d+(?:\.\d{0,2})?\z/ }, numericality: { greater_than: 0, less_than: 1000000 } + validates_numericality_of :inventory, :only_integer => true, :greater_than_or_equal_to => 0 + validates :is_active, inclusion: { in: [ true, false ] } + + + + def currency + self.price *= 1.00 + return self.price.round(2) + end + + def change_inventory(quantity_ordered) + new_inventory = self.inventory - quantity_ordered + self.inventory = new_inventory + self.save + end + + # def change_status + # if self.is_active == true + # self.update(is_active: false) + # else + # self.update(is_active: true) + # end + # + # if self.update(product_params) + # end + # end +end diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 0000000000..b517eb69ea --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,6 @@ +class Review < ApplicationRecord + belongs_to :product + validates :rating, numericality: { only_integer: true } + validates_inclusion_of :rating, numericality: true, in: (1..5) + validates :description, presence: true +end diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb new file mode 100644 index 0000000000..b9579b956c --- /dev/null +++ b/app/views/categories/new.html.erb @@ -0,0 +1,14 @@ +
+ +<%= form_with model: @category do |f| %> + +
+ <%= f.label :name %> + <%= f.text_field :name, class: "form-control" %> +
+ +
+ <%= f.submit "Add Category", class: "btn btn-info" %> +
+ <% end %> +
diff --git a/app/views/layouts/_forms_errors.html.erb b/app/views/layouts/_forms_errors.html.erb new file mode 100644 index 0000000000..7aaffedc90 --- /dev/null +++ b/app/views/layouts/_forms_errors.html.erb @@ -0,0 +1,15 @@ +<% if model.errors.messages.any? %> +
+

There were some problems. Please fix them.

+ +
+<% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000000..3b080d5975 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,112 @@ + + + + + Braineaze + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + + + + +
+ +
+ +
+ <% flash.each do | name, message | %> + <% if name == "messages" %> +

Please check the following

+ <% flash[:messages].each do |field, error_messages| %> +
<%= field.upcase %>
+ <% if error_messages.class == Array %> + <% error_messages.each do |error_message| %> +
>> <%= error_message %>
+ <% end %> + <% else %> +
>> <%= error_messages %>
+ <% end %> + +<% end %> +<% else %> +

<%= message %>

+<% end %> +<% end %> +
+ +
+ <%= yield %> +
+ + + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000000..cbd34d2e9d --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000000..37f0bddbd7 --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb new file mode 100644 index 0000000000..9fba0e2401 --- /dev/null +++ b/app/views/merchants/show.html.erb @@ -0,0 +1,100 @@ + + + +
+
+
+
+

Account Summary

+
+
    +
  • Name: <%= @current_merchant.username %>
  • +
  • Joined site on: <%= @current_merchant.created_at.strftime("%B %d, %Y")%>
  • +
  • Total Revenue: $<%= @current_merchant.revenue %>
  • +
+
+
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +

Orders

+ + + + + + + + + + + + <% if @order_items != []%> + <%@order_items.each do |item| %> + <% unless item == nil %> + + + + + + + + <% if item.status == "paid" %> + + <% end %> + + <%end%> + <%end%> + <%end%> +
Order #Purchased DateProductQuantityPriceOrder Status
<%=item.id%><%=item.created_at.strftime("%m/%d/%Y") %><%=item.product.name%><%=item.quantity%>$<%=item.quantity * item.product.price%><%=item.status%> + +
+ + + +

Merchandise

+ + + + + + + + + + <%@products.each do |product| %> + + + + + + + + <%end%> +
NameStockPriceStatus
<%=product.name%><%=product.inventory%><%= number_to_currency(product.price)%> + <%if product.is_active == true%> + + <%elsif%> + + <%end%> +
+
diff --git a/app/views/orders/edit.html.erb b/app/views/orders/edit.html.erb new file mode 100644 index 0000000000..05a2860744 --- /dev/null +++ b/app/views/orders/edit.html.erb @@ -0,0 +1,54 @@ +

Billing Information

+

Enter your billing address

+ +<%= form_with model: @shopping_cart, url: order_path(@shopping_cart.id) do |f| %> + +
+
+ <%= f.label "Cardholder's Name" %> + <%= f.text_field :cc_name, class: "form-control" %> +
+
+ +
+
+ <%= f.label :mailing_address %> + <%= f.text_field :mailing_address, class: "form-control" %> +
+
+ +
+
+ <%= f.label :email %> + <%= f.text_field :email, class: "form-control" %> +
+
+ + +
+
+ <%= f.label :Credit_Card %> + <%= f.text_field :cc, class: "form-control" %> +
+ +
+ <%= f.label :Expiration_Date %> + <%= f.text_field :cc_expiration, class: "form-control" %> +
+ +
+ <%= f.label :cvv %> + <%= f.text_field :cvv, class: "form-control" %> + +
+ +
+ <%= f.label :zip %> + <%= f.text_field :zip, class: "form-control" %> +
+
+ + + +<% end %> + diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb new file mode 100644 index 0000000000..06d3f4c585 --- /dev/null +++ b/app/views/orders/index.html.erb @@ -0,0 +1,53 @@ +

Shopping Cart

+ +
+
+ + + + + + + + + + + + <% total = 0 %> + <% @shopping_cart.order_items.each do |order_item| %> + <% product = Product.find_by(id: order_item.product_id) %> + <% total += (product.price * order_item.quantity) %> + + + + + + + + <% end %> + +
QuantityItemCostStatus
<%= link_to "x", order_item_path(order_item.id), method: :delete, class: "remove-btn", data: {confirm: 'Are you sure you want remove this item from your cart?' } %>
+
+ <%= form_with model: order_item do |f| %> + <%= f.select(:quantity, (1..order_item.product.inventory)) %> + <%= f.submit "Update Quantity", class: 'btn btn-link' %> + <% end %> + <%= order_item.product.name %><%= number_to_currency(order_item.product.price) %><%= order_item.status %>
+
+
+ +
+
+
+
+
Grand Total
+
<%= number_to_currency(total) %>
+
+ <%= link_to "Clear Cart", order_path(@shopping_cart.id), method: :delete, class: "remove-btn", data: {confirm: 'Are you sure you wan to clear your cart?' } %> +
+ +
+ <%= link_to "Checkout", checkout_path, class: "btn btn-info" %> +
+
+
diff --git a/app/views/orders/show.html.erb b/app/views/orders/show.html.erb new file mode 100644 index 0000000000..0cc4de11f0 --- /dev/null +++ b/app/views/orders/show.html.erb @@ -0,0 +1,76 @@ +

THANK YOU FOR SHOPPING AT BRAINEAZE!

+

YOUR ORDER IS COMPLETE

+ +

+ We're currently processing your order. + We will send you an email as soon as your order is shipped. +

+ +

Order Details

+ +
+
+
+
+
+
+ <%= @order.cc_name %> +
+ <%= @order.mailing_address %> +
+
+
+

+ <%= @order.created_at.strftime("%b %e, %Y") %> +

+

+ #<%= rand(1000..2000) %> +

+
+
+
+
+

Receipt

+
+ + + + + + + + + + + + <% subtotal = 0 %> + <% total = 0 %> + + <% @order.order_items.each do |order_item| %> + <% product = order_item.product %> + <% subtotal = (product.price * order_item.quantity) %> + <% total += subtotal %> + + + + + + + <% end %> + + + + + + + + + + + +
ProductQuantityPriceSubtotal
<%= product.name %><%= order_item.quantity %><%= product.price %>$<%= subtotal %>
Shipping: 
$ <%= subtotal * 0.09 %>

Total: 

$<%= total +(subtotal * 0.09) %>

+ +
+
+
diff --git a/app/views/products/_add_edit_partials.html.erb b/app/views/products/_add_edit_partials.html.erb new file mode 100644 index 0000000000..14190cca9a --- /dev/null +++ b/app/views/products/_add_edit_partials.html.erb @@ -0,0 +1,43 @@ +<%= form_with model: @product, url: product_path do |f| %> + +
+ <%= f.label :name %> + <%= f.text_field :name, class: "form-control" %> +
+ + +
+ <%= f.label :price %> + <%= f.text_field :price, class: "form-control" %> +
+ +
+ <%= f.label :quantity %> + <%= f.text_field :inventory, class: "form-control" %> +
+ +
+ <%= f.label :image %> + <%= f.text_field :image, class: "form-control" %> +
+ +
+ <%= f.label :description %> + <%= f.text_area :description, class: "form-control" %> +
+ +
+ <%= f.label :category %> +
+ <%= collection_check_boxes(:product, :category_ids, Category.all, :id, :name) do |c| %> + <%= c.label class:"label-checkbox" do%> + <%=c.check_box%> <%=c.text%> + <%end%> + <%end%> +
+
+ +
+ <%= f.submit action_name, class: "btn btn-info" %> +
+<% end %> diff --git a/app/views/products/_form.html.erb b/app/views/products/_form.html.erb new file mode 100644 index 0000000000..cd7bf5600f --- /dev/null +++ b/app/views/products/_form.html.erb @@ -0,0 +1,7 @@ +<%= render partial: 'layouts/form_errors', +locals: { + model: @product +} +%> + +<%= render partial: "form" %> diff --git a/app/views/products/edit.html.erb b/app/views/products/edit.html.erb new file mode 100644 index 0000000000..bc6f231a81 --- /dev/null +++ b/app/views/products/edit.html.erb @@ -0,0 +1,4 @@ +

Update Product

+ +<%= render partial: "add_edit_partials", +locals: { action_name: "Update Product", product_path: merchant_product_path} %> diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb new file mode 100644 index 0000000000..0e8d74453f --- /dev/null +++ b/app/views/products/index.html.erb @@ -0,0 +1,30 @@ +
+ + <% if params[:merchant_id] %> +

<%=@merchant.username%>'s Products

+ <% elsif params[:category_id] %> +

<%=@category.name%> Products

+ <% else %> + +

All Products

+ + <% end %> + +
+ <% @products.each do |product| %> +
+ <%= link_to image_tag("#{product.image}", :alt => 'product-img', :class => 'image-tag'), product_path(product.id) %> +
+

from: <%= link_to product.merchant.username, merchant_products_path(product.merchant_id) %>

+

<%= number_to_currency(product.price) %>

+
+
+ <% end %> +
+ + <% unless params[:merchant_id] || params[:category_id] %> + <%= will_paginate @products %> + <% end %> + + +
diff --git a/app/views/products/new.html.erb b/app/views/products/new.html.erb new file mode 100644 index 0000000000..410cee704c --- /dev/null +++ b/app/views/products/new.html.erb @@ -0,0 +1,4 @@ +

Add Product

+ +<%= render partial: "add_edit_partials", +locals: { action_name: "Add Product", product_path: merchant_products_path} %> diff --git a/app/views/products/root.html.erb b/app/views/products/root.html.erb new file mode 100644 index 0000000000..c5b0fd61d9 --- /dev/null +++ b/app/views/products/root.html.erb @@ -0,0 +1,46 @@ + +

From games and puzzles to toys and items for the whole family, shop for fun ways to a healthier brain.

N.E. Wan - C.E.O of BrainEaze Inc. +
+ + +
+
+

+ BrainEaze is your one stop shop for educational games, meditational resources, appeals and neurotropics and much more. +
Rejuvenate the mind and activate the nuerons! +

+
diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb new file mode 100644 index 0000000000..88ae28fd9e --- /dev/null +++ b/app/views/products/show.html.erb @@ -0,0 +1,71 @@ +
+ +
+
+

<%= @product.name %>

+
+
+

Product Reviews

+ <%= link_to "Add new review", new_product_review_path(@product.id) %> +
+
+ +
+
+

By: <%= link_to @product.merchant.username, merchants_path(@product.merchant_id) %>

+
+
+ +
+
+
+ <%= link_to image_tag("#{@product.image}", :alt => 'product-img', :class => 'image-show-tag'), product_path(@product.id) %> +
+
+
+
+ <% @product.reviews.each do |review| %> +
<%= review.rating %> out of 5
+
+
+

"<%= review.description %>"

+
+
+

<%= review.created_at.strftime("%B %d, %Y") %>

+
+ <% end %> +
+
+
+ +
+
+

Price: + <%= number_to_currency(@product.price) %> +

+
+
+ +
+
+
Product Description:
+

<%= @product.description %>

+
+
+ +
+
+
+ <%= form_with model: @shopping_cart.order_items, url: order_items_path do |f| %> + + <%= f.hidden_field :product_id, value: @product.id %> + <%= f.select(:quantity, (1..@product.inventory)) %> + + + <% end %> + + +
+
+
+
diff --git a/app/views/reviews/new.html.erb b/app/views/reviews/new.html.erb new file mode 100644 index 0000000000..621bd79d07 --- /dev/null +++ b/app/views/reviews/new.html.erb @@ -0,0 +1,43 @@ +<%= form_with model: @review, url: product_reviews_path(@review.product.id) do |f| %> + +

Add A New Review

+
+ + + + + + + +
+ <%= f.label :product_review %> + <%= f.text_area :description, :rows => 10, style: 'width:100%;' %> + + <%= f.submit "Submit", class: "btn btn-info" %> + <% end %> +
diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 0000000000..f19acf5b5c --- /dev/null +++ b/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails new file mode 100755 index 0000000000..5badb2fde0 --- /dev/null +++ b/bin/rails @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000000..d87d5f5781 --- /dev/null +++ b/bin/rake @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000000..94fd4d7977 --- /dev/null +++ b/bin/setup @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = File.expand_path('..', __dir__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/spring b/bin/spring new file mode 100755 index 0000000000..fb2ec2ebb4 --- /dev/null +++ b/bin/spring @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. + +unless defined?(Spring) + require 'rubygems' + require 'bundler' + + lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) + spring = lockfile.specs.detect { |spec| spec.name == "spring" } + if spring + Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path + gem 'spring', spring.version + require 'spring/binstub' + end +end diff --git a/bin/update b/bin/update new file mode 100755 index 0000000000..58bfaed518 --- /dev/null +++ b/bin/update @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = File.expand_path('..', __dir__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 0000000000..460dd565b4 --- /dev/null +++ b/bin/yarn @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do + begin + exec "yarnpkg", *ARGV + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000000..f7ba0b527b --- /dev/null +++ b/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000000..5c09a3eefc --- /dev/null +++ b/config/application.rb @@ -0,0 +1,25 @@ +require_relative 'boot' + +require 'rails/all' + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module Betsy + class Application < Rails::Application + config.generators do |g| + # Force new test files to be generated in the minitest-spec style + g.test_framework :minitest, spec: true + # Always use .js files, never .coffee + g.javascript_engine :js + end + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 5.2 + + # Settings in config/environments/* take precedence over those specified here. + # Application configuration can go into files in config/initializers + # -- all .rb files in that directory are automatically loaded after loading + # the framework and any gems in your application. + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000000..b9e460cef3 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,4 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. +require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000000..dd2a324c68 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: betsy_production diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc new file mode 100644 index 0000000000..0c34020ecf --- /dev/null +++ b/config/credentials.yml.enc @@ -0,0 +1 @@ +GMbIIAQ/5NRffX7iCiCBcZuW2u8rzZi5DaeBXluszYpMVKcav3AkJwB8CejAmhS94WtBtZqL9eBmTXwYr55znE7fd2qwCwJ7teizjst/V4aZAimUW1osSkxo28I6QjtHz67QZxmHhafDHZu5H7p9R04sIWeaw1sZPz59OCcYQb4XintKfAYKTkMV01P5LScnaTFwWi76KuT0xFhLd094CAFKIPcPIc4WMuMfkVsBVBa1SFSPhrHBt7dNVWH628rw1N5AlmdiybSn5lumEcJjzSQkG34xEL+83s3TZDCwMHCqZN6d0cwueJ8NiKGs3GTaP3VBLxKIz4SaEuQxOKk7JEs7ljagt/tJRx7Oz/+yxVSqg94hqYdRQKW8FBjhMnDZQvV3vRvC700I3A4yuhb1XrV2OX7HuDFdh/Lw--x2kYxhic6KQpmp1d--W1YDfTdRLPCag+7y0G50UQ== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000000..6903bb6083 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,85 @@ +# PostgreSQL. Versions 9.1 and up are supported. +# +# Install the pg driver: +# gem install pg +# On OS X with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On OS X with MacPorts: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem 'pg' +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see Rails configuration guide + # http://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + +development: + <<: *default + database: betsy_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user that initialized the database. + #username: betsy + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: betsy_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: betsy_production + username: betsy + password: <%= ENV['BETSY_DATABASE_PASSWORD'] %> diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000000..426333bb46 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 0000000000..1311e3e4ef --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,61 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join('tmp', 'caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options) + config.active_storage.service = :local + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. + config.assets.debug = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000000..5f6f3058c6 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,94 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Compress JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Store uploaded files on the local file system (see config/storage.yml for options) + config.active_storage.service = :local + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "betsy_#{Rails.env}" + + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 0000000000..0a38fd3ce9 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,46 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Store uploaded files on the local file system in a temporary directory + config.active_storage.service = :test + + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +end diff --git a/config/initializers/action_view.rb b/config/initializers/action_view.rb new file mode 100644 index 0000000000..142d382f87 --- /dev/null +++ b/config/initializers/action_view.rb @@ -0,0 +1 @@ +Rails.application.config.action_view.form_with_generates_remote_forms = false diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 0000000000..89d2efab2b --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 0000000000..4b828e80cb --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path +# Add Yarn node_modules folder to the asset load path. +Rails.application.config.assets.paths << Rails.root.join('node_modules') + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000000..59385cdf37 --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 0000000000..d3bcaa5ec8 --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy +# For further information see the following documentation +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +# Rails.application.config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https + +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end + +# If you are using UJS then enable automatic nonce generation +# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } + +# Report CSP violations to a specified URI +# For further information see the following documentation: +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only +# Rails.application.config.content_security_policy_report_only = true diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 0000000000..5a6a32d371 --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000000..4a994e1e7b --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 0000000000..ac033bf9dc --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 0000000000..dc1899682b --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000000..ef8c60c371 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,4 @@ +# config/initializers/omniauth.rb +Rails.application.config.middleware.use OmniAuth::Builder do + provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], scope: "user:email" +end diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000000..bbfc3961bf --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000000..decc5a8573 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,33 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# 'true': 'foo' +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000000..a5eccf816b --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,34 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. +# +# preload_app! + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000000..411f5ffc4f --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,40 @@ +Rails.application.routes.draw do + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + + # root 'products#index', as: 'root' + + root 'products#root', as:'root' + + get "/auth/:provider/callback", to: "sessions#create", as: 'auth_callback' + + resources :order_items, only: [:create, :update, :destroy] + + patch '/order_item/:id/update_status', to: 'order_items#update_order_item_status', as: 'update_order_item_status' + + resources :orders, only: [:show, :create, :update, :destroy] + + get '/checkout', to: 'orders#edit', as: 'checkout' + + get '/cart', to: 'orders#index', as: 'cart' + + resources :products, except: [:new, :create, :edit, :update, :destroy] do + resources :reviews, only: [:new, :create, :index] + end + + resources :categories, except: [:destroy] do + resources :products, only: [:index] + end + + resources :merchants, only: [:index] do + resources :products, except: [:show, :destroy] do + member do + post :change_status + end + end + end + + get '/dashboard', to: 'merchants#show', as: 'dashboard' + + resources :sessions, only: [:new, :create] + post '/sessions/logout', to: 'sessions#logout', as: 'logout' +end diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 0000000000..9fa7863f99 --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w[ + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +].each { |path| Spring.watch(path) } diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000000..d32f76e8fb --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket + +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/migrate/20181017223503_create_products.rb b/db/migrate/20181017223503_create_products.rb new file mode 100644 index 0000000000..f434bdf580 --- /dev/null +++ b/db/migrate/20181017223503_create_products.rb @@ -0,0 +1,11 @@ +class CreateProducts < ActiveRecord::Migration[5.2] + def change + create_table :products do |t| + t.string :name + t.float :price + t.string :description + + t.timestamps + end + end +end diff --git a/db/migrate/20181017223604_create_categories.rb b/db/migrate/20181017223604_create_categories.rb new file mode 100644 index 0000000000..6ccc3914a0 --- /dev/null +++ b/db/migrate/20181017223604_create_categories.rb @@ -0,0 +1,9 @@ +class CreateCategories < ActiveRecord::Migration[5.2] + def change + create_table :categories do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/db/migrate/20181017223750_create_join_table.rb b/db/migrate/20181017223750_create_join_table.rb new file mode 100644 index 0000000000..d5460e6b20 --- /dev/null +++ b/db/migrate/20181017223750_create_join_table.rb @@ -0,0 +1,7 @@ +class CreateJoinTable < ActiveRecord::Migration[5.2] + def change + create_join_table :products, :categories do |t| + t.index [:category_id, :product_id] + end + end +end diff --git a/db/migrate/20181017223753_create_order_items.rb b/db/migrate/20181017223753_create_order_items.rb new file mode 100644 index 0000000000..a8371730a8 --- /dev/null +++ b/db/migrate/20181017223753_create_order_items.rb @@ -0,0 +1,10 @@ +class CreateOrderItems < ActiveRecord::Migration[5.2] + def change + create_table :order_items do |t| + t.integer :quantity + t.string :status + + t.timestamps + end + end +end diff --git a/db/migrate/20181017223934_create_orders.rb b/db/migrate/20181017223934_create_orders.rb new file mode 100644 index 0000000000..3f283e3f1b --- /dev/null +++ b/db/migrate/20181017223934_create_orders.rb @@ -0,0 +1,15 @@ +class CreateOrders < ActiveRecord::Migration[5.2] + def change + create_table :orders do |t| + t.string :email + t.string :mailing_address + t.integer :cc + t.string :cc_name + t.string :cc_expiration + t.integer :cvv + t.integer :zip + + t.timestamps + end + end +end diff --git a/db/migrate/20181017224245_add_order_key_to_order_item.rb b/db/migrate/20181017224245_add_order_key_to_order_item.rb new file mode 100644 index 0000000000..140e199e1b --- /dev/null +++ b/db/migrate/20181017224245_add_order_key_to_order_item.rb @@ -0,0 +1,4 @@ +class AddOrderKeyToOrderItem < ActiveRecord::Migration[5.2] + def change + end +end diff --git a/db/migrate/20181017225013_create_merchants.rb b/db/migrate/20181017225013_create_merchants.rb new file mode 100644 index 0000000000..6fbed1393c --- /dev/null +++ b/db/migrate/20181017225013_create_merchants.rb @@ -0,0 +1,10 @@ +class CreateMerchants < ActiveRecord::Migration[5.2] + def change + create_table :merchants do |t| + t.string :username + t.string :email + + t.timestamps + end + end +end diff --git a/db/migrate/20181018003057_add_product_id_to_order_item.rb b/db/migrate/20181018003057_add_product_id_to_order_item.rb new file mode 100644 index 0000000000..cbbff4ef0f --- /dev/null +++ b/db/migrate/20181018003057_add_product_id_to_order_item.rb @@ -0,0 +1,6 @@ +class AddProductIdToOrderItem < ActiveRecord::Migration[5.2] + def change + add_reference :products, :order_items, foreign_key: true + add_reference :orders, :order_items, foreign_key: true + end +end diff --git a/db/migrate/20181018003449_create_reviews.rb b/db/migrate/20181018003449_create_reviews.rb new file mode 100644 index 0000000000..b3b1f0c445 --- /dev/null +++ b/db/migrate/20181018003449_create_reviews.rb @@ -0,0 +1,10 @@ +class CreateReviews < ActiveRecord::Migration[5.2] + def change + create_table :reviews do |t| + t.integer :rating + t.string :description + + t.timestamps + end + end +end diff --git a/db/migrate/20181018004819_add_foreign_key_to_review.rb b/db/migrate/20181018004819_add_foreign_key_to_review.rb new file mode 100644 index 0000000000..e9e799df88 --- /dev/null +++ b/db/migrate/20181018004819_add_foreign_key_to_review.rb @@ -0,0 +1,5 @@ +class AddForeignKeyToReview < ActiveRecord::Migration[5.2] + def change + add_reference :reviews, :products, foreign_key: true + end +end diff --git a/db/migrate/20181018170301_delete_bad_fkeys_for_order_items_replace_with_order_product_id.rb b/db/migrate/20181018170301_delete_bad_fkeys_for_order_items_replace_with_order_product_id.rb new file mode 100644 index 0000000000..2c3a2d144f --- /dev/null +++ b/db/migrate/20181018170301_delete_bad_fkeys_for_order_items_replace_with_order_product_id.rb @@ -0,0 +1,9 @@ +class DeleteBadFkeysForOrderItemsReplaceWithOrderProductId < ActiveRecord::Migration[5.2] + def change + remove_reference :products, :order_items, foreign_key: true + remove_reference :orders, :order_items, foreign_key: true + + add_reference :order_items, :products, foreign_key: true + add_reference :order_items, :orders, foreign_key: true + end +end diff --git a/db/migrate/20181018172853_add_merchant_id_to_products.rb b/db/migrate/20181018172853_add_merchant_id_to_products.rb new file mode 100644 index 0000000000..afcdd3c80b --- /dev/null +++ b/db/migrate/20181018172853_add_merchant_id_to_products.rb @@ -0,0 +1,5 @@ +class AddMerchantIdToProducts < ActiveRecord::Migration[5.2] + def change + add_reference :products, :merchants, foreign_key: true + end +end diff --git a/db/migrate/20181018184242_add_inventory_column_to_product.rb b/db/migrate/20181018184242_add_inventory_column_to_product.rb new file mode 100644 index 0000000000..9b4d7cd9d6 --- /dev/null +++ b/db/migrate/20181018184242_add_inventory_column_to_product.rb @@ -0,0 +1,5 @@ +class AddInventoryColumnToProduct < ActiveRecord::Migration[5.2] + def change + add_column :products, :inventory, :integer + end +end diff --git a/db/migrate/20181018190315_add_description_column_to_product.rb b/db/migrate/20181018190315_add_description_column_to_product.rb new file mode 100644 index 0000000000..00d036f8ba --- /dev/null +++ b/db/migrate/20181018190315_add_description_column_to_product.rb @@ -0,0 +1,6 @@ +class AddDescriptionColumnToProduct < ActiveRecord::Migration[5.2] + def change + remove_column :products, :description, :string + add_column :products, :description, :text + end +end diff --git a/db/migrate/20181018190541_change_description_type_to_text_in_reviews.rb b/db/migrate/20181018190541_change_description_type_to_text_in_reviews.rb new file mode 100644 index 0000000000..91fccaf06a --- /dev/null +++ b/db/migrate/20181018190541_change_description_type_to_text_in_reviews.rb @@ -0,0 +1,6 @@ +class ChangeDescriptionTypeToTextInReviews < ActiveRecord::Migration[5.2] + def change + remove_column :reviews, :description, :string + add_column :reviews, :description, :text + end +end diff --git a/db/migrate/20181018205434_change_integers_in_orders_to_strings.rb b/db/migrate/20181018205434_change_integers_in_orders_to_strings.rb new file mode 100644 index 0000000000..3d4f3cadd3 --- /dev/null +++ b/db/migrate/20181018205434_change_integers_in_orders_to_strings.rb @@ -0,0 +1,8 @@ +class ChangeIntegersInOrdersToStrings < ActiveRecord::Migration[5.2] + def change + change_column :orders, :cc, :string + change_column :orders, :cvv, :string + change_column :orders, :zip, :string + change_column :orders, :cc_expiration, :string + end +end diff --git a/db/migrate/20181018210913_fix_pluralized_foreign_keys_to_singular.rb b/db/migrate/20181018210913_fix_pluralized_foreign_keys_to_singular.rb new file mode 100644 index 0000000000..f98a45717e --- /dev/null +++ b/db/migrate/20181018210913_fix_pluralized_foreign_keys_to_singular.rb @@ -0,0 +1,10 @@ +class FixPluralizedForeignKeysToSingular < ActiveRecord::Migration[5.2] + def change + remove_reference :order_items, :products, foreign_key: true + remove_reference :order_items, :orders, foreign_key: true + + add_reference :order_items, :product, foreign_key: true + add_reference :order_items, :order, foreign_key: true + + end +end diff --git a/db/migrate/20181018212924_make_product_id_in_reviews_and_merchant_id_in_products_singular.rb b/db/migrate/20181018212924_make_product_id_in_reviews_and_merchant_id_in_products_singular.rb new file mode 100644 index 0000000000..05ef1bda80 --- /dev/null +++ b/db/migrate/20181018212924_make_product_id_in_reviews_and_merchant_id_in_products_singular.rb @@ -0,0 +1,10 @@ +class MakeProductIdInReviewsAndMerchantIdInProductsSingular < ActiveRecord::Migration[5.2] + def change + remove_reference :reviews, :products, foreign_key: true + add_reference :reviews, :product, foreign_key: true + + remove_reference :products, :merchants, foreign_key: true + + add_reference :products, :merchant, foreign_key: true + end +end diff --git a/db/migrate/20181018223818_change_price_float_to_integer_in_products.rb b/db/migrate/20181018223818_change_price_float_to_integer_in_products.rb new file mode 100644 index 0000000000..ed4eceb470 --- /dev/null +++ b/db/migrate/20181018223818_change_price_float_to_integer_in_products.rb @@ -0,0 +1,5 @@ +class ChangePriceFloatToIntegerInProducts < ActiveRecord::Migration[5.2] + def change + change_column :products, :price, :integer + end +end diff --git a/db/migrate/20181019023824_add_oauth_to_merchants.rb b/db/migrate/20181019023824_add_oauth_to_merchants.rb new file mode 100644 index 0000000000..2af9b1f1df --- /dev/null +++ b/db/migrate/20181019023824_add_oauth_to_merchants.rb @@ -0,0 +1,6 @@ +class AddOauthToMerchants < ActiveRecord::Migration[5.2] + def change + add_column :merchants, :provider, :string, null: false + add_column :merchants, :uid, :integer, null: false + end +end diff --git a/db/migrate/20181020152805_add_active_status_to_products.rb b/db/migrate/20181020152805_add_active_status_to_products.rb new file mode 100644 index 0000000000..268720166b --- /dev/null +++ b/db/migrate/20181020152805_add_active_status_to_products.rb @@ -0,0 +1,5 @@ +class AddActiveStatusToProducts < ActiveRecord::Migration[5.2] + def change + add_column :products, :is_active, :boolean, default: true + end +end diff --git a/db/migrate/20181020153054_add__status_to_orders.rb b/db/migrate/20181020153054_add__status_to_orders.rb new file mode 100644 index 0000000000..1f61361081 --- /dev/null +++ b/db/migrate/20181020153054_add__status_to_orders.rb @@ -0,0 +1,5 @@ +class AddStatusToOrders < ActiveRecord::Migration[5.2] + def change + add_column :orders, :status, :string, default: 'pending' + end +end diff --git a/db/migrate/20181020154111_add_default_status_to_order_item.rb b/db/migrate/20181020154111_add_default_status_to_order_item.rb new file mode 100644 index 0000000000..4064352183 --- /dev/null +++ b/db/migrate/20181020154111_add_default_status_to_order_item.rb @@ -0,0 +1,5 @@ +class AddDefaultStatusToOrderItem < ActiveRecord::Migration[5.2] + def change + change_column :order_items, :status, :string, default: 'pending' + end +end diff --git a/db/migrate/20181022220659_change_column_status_for_cart.rb b/db/migrate/20181022220659_change_column_status_for_cart.rb new file mode 100644 index 0000000000..887f3b738e --- /dev/null +++ b/db/migrate/20181022220659_change_column_status_for_cart.rb @@ -0,0 +1,5 @@ +class ChangeColumnStatusForCart < ActiveRecord::Migration[5.2] + def change + change_column :orders, :status, :string, default: 'cart' + end +end diff --git a/db/migrate/20181023011914_add_image_col_products.rb b/db/migrate/20181023011914_add_image_col_products.rb new file mode 100644 index 0000000000..595fd5c400 --- /dev/null +++ b/db/migrate/20181023011914_add_image_col_products.rb @@ -0,0 +1,5 @@ +class AddImageColProducts < ActiveRecord::Migration[5.2] + def change + add_column :products, :image, :string + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..4a06155752 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,89 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2018_10_23_011914) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "categories", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "categories_products", id: false, force: :cascade do |t| + t.bigint "product_id", null: false + t.bigint "category_id", null: false + t.index ["category_id", "product_id"], name: "index_categories_products_on_category_id_and_product_id" + end + + create_table "merchants", force: :cascade do |t| + t.string "username" + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "provider", null: false + t.integer "uid", null: false + end + + create_table "order_items", force: :cascade do |t| + t.integer "quantity" + t.string "status", default: "pending" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.bigint "product_id" + t.bigint "order_id" + t.index ["order_id"], name: "index_order_items_on_order_id" + t.index ["product_id"], name: "index_order_items_on_product_id" + end + + create_table "orders", force: :cascade do |t| + t.string "email" + t.string "mailing_address" + t.string "cc" + t.string "cc_name" + t.string "cc_expiration" + t.string "cvv" + t.string "zip" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "status", default: "cart" + end + + create_table "products", force: :cascade do |t| + t.string "name" + t.integer "price" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "inventory" + t.text "description" + t.bigint "merchant_id" + t.boolean "is_active", default: true + t.string "image" + t.index ["merchant_id"], name: "index_products_on_merchant_id" + end + + create_table "reviews", force: :cascade do |t| + t.integer "rating" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "description" + t.bigint "product_id" + t.index ["product_id"], name: "index_reviews_on_product_id" + end + + add_foreign_key "order_items", "orders" + add_foreign_key "order_items", "products" + add_foreign_key "products", "merchants" + add_foreign_key "reviews", "products" +end diff --git a/db/seed_data/categories.csv b/db/seed_data/categories.csv new file mode 100644 index 0000000000..173120713d --- /dev/null +++ b/db/seed_data/categories.csv @@ -0,0 +1,12 @@ +name +toy +book +puzzle +Family +Physical +Active +Creative +Dexterity +Party +Skilled +Strategic diff --git a/db/seed_data/merchants.csv b/db/seed_data/merchants.csv new file mode 100644 index 0000000000..703ac49f00 --- /dev/null +++ b/db/seed_data/merchants.csv @@ -0,0 +1,9 @@ +username,email,uid,provider +Angela,angelab@someemail.com,12234,github +susan,susan@someemail.com,45678,github +lula,lulasam@someemail.com,56777,github +James,james@someemail.com,65933,github +Samantha,samantha@someemail.com,45982,github +Sally,sally3@someemail.com,65389,github +Michael,m_james@someemail.com,98129,github +David,d_smith@someemail.com,87234,github diff --git a/db/seed_data/orders.csv b/db/seed_data/orders.csv new file mode 100644 index 0000000000..4d08addcf4 --- /dev/null +++ b/db/seed_data/orders.csv @@ -0,0 +1,7 @@ +email,mailing_address,cc,cc_name,cc_expiration,cvv,zip,status +someone@mail.com,1234 S main St Seattle WA 98134,1234567890123456,Samantha Smith,11/20,567,98134,paid +secondone@mail.com,5432 S Republican St Seattle WA 98174,9876543212345678,Ben Tyler,11/22,765,98174,completed +another@mail.com,5432 S Republican St Seattle WA 98174,9876543212345678,Ben Tyler,11/22,765,98174,paid +fourthperson@mail.com,4564 S Roy St Seattle WA 98184,9876543212345678,Clementin Blue,12/22,509,98184,completed +person_sam@mail.com,7282 S Smith St Seattle WA 98184,9876543216789654,Lula Ross,12/23,344,98184,canceled +person_chris@mail.com,7282 N Alabama St Portland OR 94568,9876543216789654,Chris Roberts,09/23,231,94568,paid diff --git a/db/seed_data/products.csv b/db/seed_data/products.csv new file mode 100644 index 0000000000..067d6a3d70 --- /dev/null +++ b/db/seed_data/products.csv @@ -0,0 +1,12 @@ +name,price,inventory,description,image +Tic Tac Toe,20,50,Beloved childhood game making space in a 3x3 grid. Player wins when they succeeds in placing three of their marks in horizontal vertical or diagonal row.,https://images-na.ssl-images-amazon.com/images/I/81FZ9tL%2B0QL._SX466_.jpg +Drink-Tac-Toe,45,50,Wine beer play out the timeless classic game of tic tac toe.,https://img.papyrusonline.com/media/catalog/product/LCI/M2/Fi/YTQ1NjRlMTRmMTE5YTNkODJlMDgwN2U2N2M1YjU0OWMzNDQ5ZmYwNGZmYTA0YjdiMzlmNzAzODQyNjcyNjp7ImRzIjoiaW1hZ2UiLCJmIjoiXC84XC81XC84NTgwNjRfZC5qcGciLCJmYSI6dHJ1ZSwiZnEiOjE2LCJmdCI6dHJ1ZSwiZnciOjcyMH0~.jpg +Otrio,50,20,This is an excellent 2-4 player head strategy game that will fire everyones competitive side.,https://target.scene7.com/is/image/Target/GUEST_4dcbcc59-1e6f-4753-ada0-69d3a5871880?wid=488&hei=488&fmt=pjpeg +Hasbro Monopoly Classic Game,15,50,Buy sell dream and scheme your way to riches.,https://refreshcartridges.co.uk/productimages/a_126835.jpg +Kanoodle - Brain Twisting Solitaire game,7,50,Perfect for developing problem solving and strategic thinking skills.,https://images.currentcatalog.com/catalog/product/450x450/V2978/v2978_lv811.jpg +BrainGames,12,40,Put your brain to test with this engaging game that boasts a variety of logic challenges.,https://zcdn.freetls.fastly.net/images/cache/product//161712/zu35321091_alt_1_tm1452738711.jpg +Flower Frenzy Puzzle,16.99,40,Enjoy this art blossom as family and friends piece together this collage of bright flowers.,https://images.currentcatalog.com/catalog/product/614552/flower-frenzy-puzzle.jpg +Pac Man game,24.99,50,As much fun as the action packed arcade game of the same name.,https://geekandsundry.com/wp-content/uploads/2016/09/919-8ymoxNL._SL1500_-680x388.jpg +Newtown,32.99,40,Defy the laws of grabity and add a magical twist to a familiar concept. All you gotta do is get 5 balls in a row.,https://d1whcn1ntmec99.cloudfront.net/images/catalog/products/marbles/newton/full1.jpg +ChessGame,29.99,50,Play chess with family and friends.,https://images.unsplash.com/photo-1528819622765-d6bcf132f793?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8cf14450e7ab6e30d85800342f9ed485&auto=format&fit=crop&w=2250&q=80 +Bean Bag Toss Game,12.29,10,stack em up and knock em down! This game is great for kids.,https://www.carnivalsavers.com/product_images/uploaded_images/can-knock-down-backyard-carnival-game-to-buy.jpg diff --git a/db/seed_data/reviews.csv b/db/seed_data/reviews.csv new file mode 100644 index 0000000000..0949bd0ff6 --- /dev/null +++ b/db/seed_data/reviews.csv @@ -0,0 +1,12 @@ +rating,description +2,This game was not as challenging as I hoped for. +5,I love this game. We played it as children with my siblings and it was so much fun. +3,It was okay. I was hoping my kids would be into it but they did not enjoy it as much. +1,It broke the minute I took it out of the packaging. It is made with cheap material. +5,Loads of fun. +1,Not challenging enough. +1,Did not like it +4,It was great +4,My kids loved it +1,Too expensive and not boring. +4,Love it! diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000000..97abbd0af4 --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,174 @@ +require 'csv' + +MERCHANT_FILE = Rails.root.join('db', 'seed_data', 'merchants.csv') +puts "Loading raw merchant data from #{MERCHANT_FILE}" + +merchant_failures = [] + +CSV.foreach(MERCHANT_FILE, :headers => true) do |row| + merchant = Merchant.new + merchant.id = row['id'] + merchant.username = row['username'] + merchant.email = row['email'] + merchant.uid = row['uid'] + merchant.provider = row['provider'] + + successful = merchant.save + if !successful + merchant_failures << merchant + puts "Failed to save merchants: #{merchant.inspect}" + + else + puts "Created merchant: #{merchant.inspect}" + end +end + +puts "Added #{Merchant.count} merchant records" +puts "#{merchant_failures.length} merchants failed to save" + + + +CATEGORY_FILE = Rails.root.join('db', 'seed_data', 'categories.csv') +puts "Loading raw category data from #{CATEGORY_FILE}" + +category_failures = [] + +CSV.foreach(CATEGORY_FILE, :headers => true) do |row| + category = Category.new + category.name = row['name'] + + successful = category.save + if !successful + category_failures << category + puts "Failed to save categories: #{category.inspect}" + + else + puts "Created category: #{category.inspect}" + end +end + +puts "Added #{Category.count} category records" +puts "#{category_failures.length} categories failed to save" + + +PRODUCTS_FILE = Rails.root.join('db', 'seed_data', 'products.csv') +puts "Loading raw product data from #{PRODUCTS_FILE}" + +product_failures = [] + +CSV.foreach(PRODUCTS_FILE, :headers => true) do |row| + product = Product.new + + product.name = row['name'] + product.price = row['price'] + product.inventory = row['inventory'] + product.description = row['description'] + product.image = row['image'] + product.merchant_id = Merchant.all.sample.id + product.categories << Category.all.sample(rand(1..3)) + + + successful = product.save + if !successful + product_failures << product + puts "Failed to save product: #{product.inspect}" + + else + puts "Created products: #{product.inspect}" + end +end + +puts "Added #{Product.count} product records" +puts "#{product_failures.length} products failed to save" + + +ORDERS_FILE = Rails.root.join('db', 'seed_data', 'orders.csv') +puts "Loading raw order data from #{ORDERS_FILE}" + +order_failures = [] + +CSV.foreach(ORDERS_FILE, :headers => true) do |row| + order = Order.new + order.email = row['email'] + order.mailing_address = row['mailing_address'] + order.cc = row['cc'] + order.cc_name = row['cc_name'] + order.cc_expiration = row['cc_expiration'] + order.cvv = row['cvv'] + order.zip = row['zip'] + order.status = row['status'] + + successful = order.save + if !successful + order_failures << order + puts "Failed to save order: #{order.inspect}" + + else + puts "Created orders: #{order.inspect}" + end +end + +puts "Added #{Order.count} order records" +puts "#{order_failures.length} orders failed to save" + + + +# ORDERITEMS_FILE = Rails.root.join('db', 'seed_data', 'order_items.csv') +# puts "Loading raw order item data from #{ORDERITEMS_FILE}" + +order_items_failures = [] + +# random_orders = [] +# random_orders << Order.where.not(status:'cart') +random_orders = Order.all.sample(5) + +random_orders.each do |order| + sample_products = Product.all.sample(rand(1..5)) + sample_products.each do |product| + order_item = OrderItem.new + order_item.order_id = order.id + order_item.product_id = product.id + order_item.quantity = rand(1...10) + order_item.status = case order.status + when "cart"; "pending" + when "paid"; "paid" + when "completed"; "shipped" + end + successful = order_item.save + + if !successful + order_items_failures << order_item + puts "Failed to save order_items: #{order_item.inspect}" + + else + puts "Created order_item: #{order_item.inspect}" + end + + end +end + + +REVIEWS_FILE = Rails.root.join('db', 'seed_data', 'reviews.csv') +puts "Loading raw review data from #{REVIEWS_FILE}" + +review_failures = [] + +CSV.foreach(REVIEWS_FILE, :headers => true) do |row| + review = Review.new + review.rating = row['rating'] + review.description = row['description'] + review.product_id = (Product.all.sample).id + + + successful = review.save + if !successful + review_failures << review + puts "Failed to save reviews: #{review.inspect}" + + else + puts "Created reviews: #{review.inspect}" + end +end + +puts "Added #{Review.count} review records" +puts "#{review_failures.length} reviews failed to save" diff --git a/lib/assets/.keep b/lib/assets/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/log/.keep b/log/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/package.json b/package.json new file mode 100644 index 0000000000..f874acf437 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "name": "betsy", + "private": true, + "dependencies": {} +} diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000000..2be3af26fc --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/422.html b/public/422.html new file mode 100644 index 0000000000..c08eac0d1d --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/500.html b/public/500.html new file mode 100644 index 0000000000..78a030af22 --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/brain.png b/public/brain.png new file mode 100644 index 0000000000..f7f44f63bc Binary files /dev/null and b/public/brain.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000..fc4ce1204e Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000000..37b576a4a0 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/storage/.keep b/storage/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 0000000000..d19212abd5 --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb new file mode 100644 index 0000000000..c0cbf275e8 --- /dev/null +++ b/test/controllers/categories_controller_test.rb @@ -0,0 +1,65 @@ +require "test_helper" + +describe CategoriesController do + let(:one) { merchants(:one) } + + + describe "logged in users" do + it "should be able to access new category page" do + login(one) + get new_category_path + must_respond_with :success + end + + it "should be able to create a new category with valid data" do + login(one) + data = { + category: { + name: 'zombie food' + } + } + + category = Category.new(data[:category]) + category.must_be :valid? + expect { + post categories_path, params: data + }.must_change('Category.count', +1) + + must_respond_with :redirect + must_redirect_to categories_path(Category.last.id) + end + + it "should be responded with a bad request for a non-unique category name" do + login(one) + data = {category: {name: 'toy'}} + category = Category.new(data[:category]) + expect { + post categories_path, params: data + }.wont_change('Category.count') + + must_respond_with :bad_request + end + end + + + describe "guest users" do + it "should not be able to access new category page" do + get new_category_path + must_respond_with :redirect + must_redirect_to root_path + end + + it "should not be able to create a new category" do + data = {category: {name: 'bad category'}} + + category = Category.new(data[:category]) + + expect { + post categories_path, params: data + }.wont_change('Category.count') + + must_respond_with :redirect + must_redirect_to root_path + end + end +end diff --git a/test/controllers/merchants_controller_test.rb b/test/controllers/merchants_controller_test.rb new file mode 100644 index 0000000000..ebe853e69c --- /dev/null +++ b/test/controllers/merchants_controller_test.rb @@ -0,0 +1,14 @@ +require "test_helper" + +describe MerchantsController do + describe "show" do + let(:one) { merchants(:one) } + let(:four) { merchants(:four) } + + it "goes to dashboard view when logged in" do + login(one) + get dashboard_path + must_respond_with :success + end + end +end diff --git a/test/controllers/order_items_controller_test.rb b/test/controllers/order_items_controller_test.rb new file mode 100644 index 0000000000..2570d22d5d --- /dev/null +++ b/test/controllers/order_items_controller_test.rb @@ -0,0 +1,370 @@ +require "test_helper" + +describe OrderItemsController do + let(:plushie) {products(:plushie)} + let(:plushie_merchant) {merchants(:one)} + let(:not_plushie_merchant) {merchants(:two)} + + describe "Create" do + it "it can create an Order Item (add item to cart) for a Guest user" do + product = plushie + qty_requested = product.inventory + order_item_data = { + order_item: { + quantity: qty_requested, + product_id: product.id + } + } + expect { + post order_items_path, params: order_item_data + }.must_change('OrderItem.count', +1) + + OrderItem.last.quantity.must_equal qty_requested + must_respond_with :redirect + must_redirect_to cart_path + end + + it "it can create an Order Item (add item to cart) for a Merchant that doesn't have the product" do + login(not_plushie_merchant) + product = plushie + qty_requested = product.inventory + order_item_data = { + order_item: { + quantity: qty_requested, + product_id: product.id + } + } + expect { + post order_items_path, params: order_item_data + }.must_change('OrderItem.count', +1) + + OrderItem.last.quantity.must_equal qty_requested + must_respond_with :redirect + must_redirect_to cart_path + end + + it "renders 404 not_found for a bogus order_item data when trying to add to cart" do + order = Order.first + product = plushie + + order_item_data = { + order_item: { + quantity: 0, + product_id: product.id + } + } + + expect { + post order_items_path, params: order_item_data + }.wont_change('OrderItem.count') + + must_redirect_to root_path + end + + it "renders a bad request if the quantity requested is more than the product inventory" do + order = Order.first + product = plushie + bad_qty = product.inventory + 1 + order_item_data = { + order_item: { + quantity: bad_qty, + product_id: product.id + } + } + + expect { + post order_items_path, params: order_item_data + }.wont_change('OrderItem.count') + must_redirect_to root_path + end + + it "updates the qty requested if the order item is already in the cart for a Guest user" do + product = plushie + qty_requested = 7 + order_item_data = { + order_item: { + quantity: qty_requested, + product_id: product.id + } + } + + post order_items_path, params: order_item_data + + expect { + post order_items_path, params: order_item_data + }.wont_change('OrderItem.count') + + OrderItem.last.quantity.must_equal qty_requested * 2 + must_respond_with :redirect + must_redirect_to cart_path + end + + it "updates the qty requested if the order item is already in the cart for a merchant who doesn't own the product" do + login(not_plushie_merchant) + product = plushie + qty_requested = 7 + order_item_data = { + order_item: { + quantity: qty_requested, + product_id: product.id + } + } + + post order_items_path, params: order_item_data + + expect { + post order_items_path, params: order_item_data + }.wont_change('OrderItem.count') + + OrderItem.last.quantity.must_equal qty_requested * 2 + must_respond_with :redirect + must_redirect_to cart_path + end + + it "redirects the merchant if they try to purchase their own product" do + login(plushie_merchant) + product = plushie + qty_requested = 7 + order_item_data = { + order_item: { + quantity: qty_requested, + product_id: product.id, + } + } + expect { + post order_items_path, params: order_item_data + }.wont_change('OrderItem.count') + + must_respond_with :redirect + must_redirect_to dashboard_path + end + end + + describe "Update" do + it "successfully updates the Order Item quantity with a pending status" do + order_item = order_items(:pending) + original_qty = order_item.quantity + updated_qty = original_qty + 1 + + patch order_item_path(order_item.id), params: { + order_item: { + quantity: updated_qty + } + } + order_item.reload + order_item.quantity.must_equal updated_qty + must_redirect_to cart_path + end + + it "renders a 404 not_found for bogus order item data" do + order_item = order_items(:pending) + original_qty = order_item.quantity + + patch order_item_path(order_item.id), params: { + order_item: { + quantity: 'four' + } + } + must_redirect_to root_path + order_item.quantity.must_equal original_qty + end + + it "renders a bad request if the quantity updated is more than the product inventory" do + order_item = order_items(:pending) + original_qty = order_item.quantity + bad_qty = original_qty + 100 + + patch order_item_path(order_item.id), params: { + order_item: { + quantity: bad_qty + } + } + + order_item.quantity.must_equal original_qty + must_redirect_to root_path + end + + it "renders a bad request if the quantity updated is good but the status is paid" do + paid_order_item = order_items(:paid) + original_qty = paid_order_item.quantity + good_qty = original_qty + 1 + + patch order_item_path(paid_order_item.id), params: { + order_item: { + quantity: good_qty + } + } + + paid_order_item.quantity.must_equal original_qty + must_redirect_to root_path + end + + it "renders a bad request if the quantity updated is good but the status is cancelled" do + paid_order_item = order_items(:cancelled) + original_qty = paid_order_item.quantity + good_qty = original_qty + 1 + + patch order_item_path(paid_order_item.id), params: { + order_item: { + quantity: good_qty + } + } + + paid_order_item.quantity.must_equal original_qty + must_redirect_to root_path + end + + it "renders a bad request if the quantity updated is good but the status is shipped" do + paid_order_item = order_items(:shipped) + original_qty = paid_order_item.quantity + good_qty = original_qty + 1 + + patch order_item_path(paid_order_item.id), params: { + order_item: { + quantity: good_qty + } + } + + paid_order_item.quantity.must_equal original_qty + must_redirect_to root_path + end + end + + describe "Update Order Item Status" do + it "updates the status to shipped if it has the right merchant associated with the order item" do + paid_order_item = order_items(:paid) + merchant = paid_order_item.merchant + login(merchant) + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "shipped" } + + paid_order_item.reload + paid_order_item.status.must_equal "shipped" + must_redirect_to dashboard_path + end + + it "updates the status to cancelled if it has the right merchant associated with the order item" do + paid_order_item = order_items(:paid) + merchant = paid_order_item.merchant + login(merchant) + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "cancelled" } + + paid_order_item.reload + paid_order_item.status.must_equal "cancelled" + must_redirect_to dashboard_path + end + + it "does not update the status if a Merchant tries to update an order item to shipped that is not associated with them" do + paid_order_item = order_items(:paid) + merchant = plushie_merchant + login(merchant) + good_qty = paid_order_item.quantity + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "shipped" } + + paid_order_item.reload + paid_order_item.status.must_equal "paid" + must_redirect_to dashboard_path + end + + it "does not update the status if a Merchant tries to update an order item to shipped that is not associated with them" do + paid_order_item = order_items(:paid) + merchant = plushie_merchant + login(merchant) + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "cancelled" } + + paid_order_item.reload + paid_order_item.status.must_equal "paid" + must_redirect_to dashboard_path + end + + it "does not update the status if a guest user tries to change the status to shipped" do + paid_order_item = order_items(:paid) + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "shipped" } + + paid_order_item.reload + paid_order_item.status.must_equal "paid" + must_redirect_to dashboard_path + + end + + it "does not update the status if a guest user tries to change the status to cancelled" do + paid_order_item = order_items(:paid) + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "cancelled" } + + paid_order_item.reload + paid_order_item.status.must_equal "paid" + must_redirect_to dashboard_path + end + + it "does not update the status for a bogus status with a logged in merchant who has the product" do + paid_order_item = order_items(:paid) + merchant = paid_order_item.merchant + login(merchant) + paid_order_item.status.must_equal "paid" + + patch update_order_item_status_path(paid_order_item.id), params: { status: "bogus" } + + paid_order_item.reload + paid_order_item.status.must_equal "paid" + must_redirect_to dashboard_path + end + end + + describe "Destroy" do + it "deletes an order item when status is pending" do + order_item = order_items(:pending) + order = order_item.order + + expect{ + delete order_item_path(order_item.id) + }.must_change('order.order_items.count', -1) + + must_respond_with :redirect + must_redirect_to cart_path + end + + it "does not delete order item if status is paid" do + order_item = order_items(:paid) + order = order_item.order + + expect { + delete order_item_path(order_item.id) + }.wont_change('order.order_items.count') + + must_redirect_to root_path + end + + it "does not delete order item if status is cancelled" do + order_item = order_items(:cancelled) + order = order_item.order + + expect { + delete order_item_path(order_item.id) + }.wont_change('order.order_items.count') + + must_redirect_to root_path + end + + it "does not delete order item if status is shipped" do + order_item = order_items(:shipped) + order = order_item.order + + expect { + delete order_item_path(order_item.id) + }.wont_change('order.order_items.count') + + must_redirect_to root_path + end + end +end diff --git a/test/controllers/orders_controller_test.rb b/test/controllers/orders_controller_test.rb new file mode 100644 index 0000000000..bb8a26784e --- /dev/null +++ b/test/controllers/orders_controller_test.rb @@ -0,0 +1,183 @@ +require "test_helper" + +describe OrdersController do + let(:order_one) {orders(:one)} + let(:order_three) {orders(:three)} + + describe "index" do + it "should get index" do + get cart_path + must_respond_with :success + end + end + + describe "show" do + it "should respond with success for showing an existing order with order_items " do + get order_path(order_three.id) + must_respond_with :success + end + + it "should respond with not found for showing a non-existing order" do + order = orders(:two) + + order.order_items.destroy_all + order.destroy + + get order_path(order.id) + must_respond_with :not_found + end + end + + describe "edit" do + it "responds with success for an existing order" do + product = products(:plushie) + order_item_data = { + order_item: { + quantity: 7, + product_id: product.id + } + } + + post order_items_path, params: order_item_data + get checkout_path + must_respond_with :success + end + + it "responds with error, cart is empty and redirects to root if try to edit empty cart." do + get checkout_path + must_respond_with :redirect + end + end + + describe "update" do + before do + @order = orders(:four) + end + + it "Updates the order billing params successfully" do + @order.email.must_equal nil + @order.mailing_address.must_equal nil + @order.cc.must_equal nil + @order.cc_name.must_equal nil + @order.cc_expiration.must_equal nil + @order.cvv.must_equal nil + @order.zip.must_equal nil + @order.status.must_equal 'cart' + + put order_path(@order.id), params:{ + order: { + email: "bbb@gmail.com", + mailing_address: '2334 B Street', + cc: 1234123412341234, + cc_name: 'Some Name', + cc_expiration: '10/23', + cvv: 987, + zip: 98123, + } + } + + @order.reload + + updated_order = Order.find_by(id: @order.id) + updated_order.email.must_equal "bbb@gmail.com" + updated_order.mailing_address.must_equal "2334 B Street" + updated_order.cc.must_equal "1234123412341234" + updated_order.cc_name.must_equal "Some Name" + updated_order.cc_expiration.must_equal "10/23" + updated_order.cvv.must_equal "987" + updated_order.zip.must_equal "98123" + updated_order.status.must_equal 'paid' + must_redirect_to order_path(@order.id) + end + + + it "will change the status of each order item from 'pending' to 'paid'" do + + @order.order_items.each do |order_item| + order_item.status.must_equal 'pending' + end + + patch order_path(@order.id), params:{ + order: { + email: "bbb@gmail.com", + mailing_address: '2334 B Street', + cc: 1234123412341234, + cc_name: 'Some Name', + cc_expiration: '10/23', + cvv: 987, + zip: 98123, + } + } + + @order.reload + @order.order_items.each do |order_item| + order_item.status.must_equal 'paid' + end + end + + it "will change the inventory for each product placed in the order after it has been updated as paid" do + + first_order_item = @order.order_items.first + first_product = first_order_item.product + original_inventory = first_product.inventory + requested_qty = first_order_item.quantity + + patch order_path(@order.id), params:{ + order: { + email: "bbb@gmail.com", + mailing_address: '2334 B Street', + cc: 1234123412341234, + cc_name: 'Some Name', + cc_expiration: '10/23', + cvv: 987, + zip: 98123, + } + } + + @order.reload + first_order_item = @order.order_items.first + first_product = first_order_item.product + updated_inventory = first_product.inventory + updated_inventory.must_equal original_inventory - requested_qty + + end + + it "will redirect with a bad request if there is missing data" do + put order_path(@order.id), params:{ + order: { + email: nil, + mailing_address: '2334 B Street', + cc: nil, + cc_name: 'Name', + cc_expiration: nil, + cvv: nil, + zip: nil, + } + } + + updated_order = Order.find_by(id: @order.id) + updated_order.email.must_equal nil + updated_order.mailing_address.must_equal nil + updated_order.cc.must_equal nil + updated_order.cc_name.must_equal nil + updated_order.cc_expiration.must_equal nil + updated_order.cvv.must_equal nil + updated_order.zip.must_equal nil + updated_order.status.must_equal 'cart' + + must_redirect_to checkout_path + end + end + + describe "destroy" do + it "can clear the cart successfully" do + order = orders(:three) + before_order_items_count = order.order_items.count + + expect { + delete order_path(order) + }.must_change('puts "inside the must_change argument"; OrderItem.count', -1) + must_redirect_to cart_path + end + end +end diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb new file mode 100644 index 0000000000..4f07c7768e --- /dev/null +++ b/test/controllers/products_controller_test.rb @@ -0,0 +1,180 @@ +require "test_helper" + +describe ProductsController do + let(:one) {merchants(:one)} + let(:bad_product_id) {Product.first.destroy.id} + + describe 'index' do + it "should get index for a guest" do + get products_path + must_respond_with :success + end + + it "should get index for a logged-in user" do + login(one) + get products_path + must_respond_with :success + end + + end + + describe 'show' do + it "should get show for a guest" do + id = products(:puzzle) + get product_path(id) + must_respond_with :success + end + + it "should get show for a logged-in user" do + login(one) + id = products(:puzzle) + get product_path(id) + must_respond_with :success + end + + it "should respond with not found for showing a non-existing product for a guest" do + get product_path(bad_product_id) + must_respond_with :not_found + end + + it "should respond with not found for showing a non-existing product for a logged-in user" do + login(one) + get product_path(bad_product_id) + must_respond_with :not_found + end + end + + describe 'new' do + it 'should get a new form to add products' do + login(one) + get new_merchant_product_path(one.id) + must_respond_with :success + end + end + + describe 'create' do + it 'it can create a product with valid data' do + login(one) + product_data = { + product: { + name: "some toy", + price: 25, + inventory: 10, + merchant_id: one.id, + description: "An integlligent brain plushie", + category_ids: [categories(:toy).id] + } + } + + test_product = Product.new(product_data[:product]) + valid = test_product.valid? + valid.must_equal true, "Product data was invalid: #{test_product.errors.messages}, please come fix this test" + + expect{ + post merchant_products_path(one.id), params: product_data + }.must_change('Product.count', +1) + + must_redirect_to product_path(Product.last) + end + + it 'does not create a new product with invalid data' do + login(one) + + product_data = { + product: { + merchant: merchants(:one), + category_ids: [categories(:toy).id] + } + } + + Product.new(product_data[:product]).wont_be :valid? + + expect { + post(merchant_products_path(one.id), params: product_data) + }.wont_change('Product.count') + + must_respond_with :bad_request + end + end + + describe "edit" do + it "responds with success for an existing book" do + login(one) + product = Product.first + get edit_merchant_product_path(one.id, product.id) + must_respond_with :success + end + + it "responds with not_found for a book that D.N.E." do + login(one) + get edit_merchant_product_path(one.id, bad_product_id) + must_respond_with :not_found + end + end + + describe 'update' do + it "updates with valid data with a logged-in user" do + login(one) + + existing_product = products(:puddy) + + put merchant_product_path(one.id, existing_product.id), params: { + product: { + name: "Brain Puddy UPDATED", + price: 14, + description: "Updated Description!", + inventory: 100 + } + } + + updated_product = Product.find_by(id: existing_product.id) + + updated_product.name.must_equal "Brain Puddy UPDATED" + must_respond_with :redirect + must_redirect_to product_path(updated_product.id) + end + + it "fails for bogus data and aproduct with a logged-in user" do + login(one) + + existing_product = products(:puddy) + + put merchant_product_path(one.id, existing_product.id), params: { + product: { + name: "", + price: 14, + description: "Updated Description!", + inventory: 100 + } + } + + unchanged_product = Product.find_by(id: existing_product.id) + + unchanged_product.name.must_equal "Brain Puddy" + must_respond_with :bad_request + end + end + + describe 'change product status' do + it 'it should change the status with post request' do + login(one) + + existing_product = products(:puddy) + + post change_status_merchant_product_path(one.id, existing_product.id), params: { + product: { + name: "Brain Puddy", + price: 14, + description: "Description!", + inventory: 100, + is_active: false + } + } + + updated_product = Product.find_by(id: existing_product.id) + + updated_product.is_active.must_equal false + must_redirect_to dashboard_path + end + end +end diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb new file mode 100644 index 0000000000..da7d3b02d7 --- /dev/null +++ b/test/controllers/reviews_controller_test.rb @@ -0,0 +1,110 @@ +require "test_helper" + +describe ReviewsController do + let(:one) { merchants(:one)} + let (:two) {merchants(:two)} + let(:plushie) { products(:plushie) } + + describe "new" do + describe "logged in users" do + it "get to a new review page" do + login(one) + get new_product_review_path(plushie.id) + must_respond_with :success + end + + it "can't get to a new review page for non-existent product" do + login(one) + plushie.destroy + get new_product_review_path(plushie.id) + must_respond_with :not_found + end + end + + describe "guest users" do + it "can get to a new review page" do + get new_product_review_path(plushie.id) + must_respond_with :success + end + + it "can't get to a new review page for non-existent product" do + plushie.destroy + get new_product_review_path(plushie.id) + must_respond_with :not_found + end + end + + end + + describe "create" do + let (:good_data) { {review: {rating: 5, product: plushie, description: 'luv it!!'} } } + let (:bad_data) { {review: {rating: -5, product: plushie, description: 'hate it!! :( '} } } + let (:bad_data2) { {review: {rating: 1, product: plushie, description: ''} } } + + describe "logged in users" do + it "can submit review for products they don't sell" do + login(two) + expect{ + post product_reviews_path(plushie.id), params: good_data + }.must_change('Review.count', +1) + + must_redirect_to product_path(plushie.id) + + end + + it "can't submit review with invalid rating" do + login(two) + expect{ + post product_reviews_path(plushie.id), params: bad_data + }.wont_change('Review.count') + must_respond_with :bad_request + end + + it "can't submit review with invalid description" do + login(two) + expect{ + post product_reviews_path(plushie.id), params: bad_data2 + }.wont_change('Review.count') + must_respond_with :bad_request + end + + it "can't submit a review for products they sell" do + login(one) + + expect{ + post product_reviews_path(plushie.id), params: good_data + }.wont_change('Review.count') + + must_respond_with :redirect + must_redirect_to dashboard_path + + + end + end + + describe "guest users" do + it "guest users can submit review" do + expect{ + post product_reviews_path(plushie.id), params: good_data + }.must_change('Review.count', +1) + + must_redirect_to product_path(plushie.id) + end + + it "can't submit review with invalid rating" do + expect{ + post product_reviews_path(plushie.id), params: bad_data + }.wont_change('Review.count') + must_respond_with :bad_request + end + + it "can't submit review with invalid description" do + expect{ + post product_reviews_path(plushie.id), params: bad_data2 + }.wont_change('Review.count') + must_respond_with :bad_request + end + end + + end +end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb new file mode 100644 index 0000000000..5a4f757d2e --- /dev/null +++ b/test/controllers/sessions_controller_test.rb @@ -0,0 +1,85 @@ +require "test_helper" + +describe SessionsController do + + describe "login" do + it "can successfully log in with githun as an exisiting user and redirects to root path" do + + #Arrange + # Make sure that for some existing user, everything is configured + + merchant_one = merchants(:one) + + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( merchant_one ) ) + + #Act + # Simulating logging in with github being successful (given the OmniAUth hash above) + get auth_callback_path(:github) + + # Assert + #Check that it redirects + must_redirect_to root_path + + + expect(flash[:success]).must_equal "Logged in as returning user #{merchant_one.username}" + end + + it 'creates a new user successfully when logging in with a new valid merchant' do + + start_merch_count = Merchant.count + + new_merchant = Merchant.new(username: "nicodimos", uid: 56, provider: :github, email: "nico@zzz.com") + + expect(new_merchant.valid?).must_equal true + + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( new_merchant) ) + + get auth_callback_path(:github) + + must_redirect_to root_path + expect( Merchant.count ).must_equal start_merch_count + 1 + expect( session[:merchant_id] ).must_equal Merchant.last.id + expect( flash[:success] ).must_equal "Logged in Successfully. Welcome #{new_merchant.username}" + end + + it 'does not create a new merchant when logging in with a new invalid merchant' do + + start_merch_count = Merchant.count + + invalid_new_merchant = Merchant.new(username: nil, uid: nil, provider: :github, email: "nico@zzz.com") + + expect(invalid_new_merchant.valid?).must_equal false + + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( invalid_new_merchant ) ) + + get auth_callback_path(:github) + + must_redirect_to root_path + expect( Merchant.count ).must_equal start_merch_count + expect( session[:merchant_id] ).must_equal nil + expect( flash[:error] ).must_equal "Could not create new account: #{invalid_new_merchant.errors.messages}" + end + end + describe 'logout' do + before do + merchant_one = merchants(:one) + + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( merchant_one ) ) + + get auth_callback_path(:github) + end + it 'can successfully logout by clearing the session' do + + post logout_path(session[:merchant_id]) + + expect( session[:merchant_id]).must_equal nil + expect( flash[:success] ).must_equal "Successfully logged out" + end + + it 'should redirect to the root page' do + post logout_path(session[:merchant_id]) + + must_redirect_to root_path + end + end +end diff --git a/test/fixtures/.keep b/test/fixtures/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml new file mode 100644 index 0000000000..9a5d032e34 --- /dev/null +++ b/test/fixtures/categories.yml @@ -0,0 +1,13 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +toy: + name: toy + +book: + name: book + +meditation: + name: meditation + +apparel: + name: apparel diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/merchants.yml b/test/fixtures/merchants.yml new file mode 100644 index 0000000000..2cb3fe49a1 --- /dev/null +++ b/test/fixtures/merchants.yml @@ -0,0 +1,25 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + username: griffiam + email: sesamestreet@yahoo.com + uid: 12345678 + provider: github + +two: + username: yggurlie + email: yggurlie@yahoo.com + uid: 87654321 + provider: github + +three: + username: maddiemax + email: maddiemaxwell@yahoo.com + uid: 23456789 + provider: github + +four: + username: boing + email: boing@yahoo.com + uid: 23456732 + provider: github diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml new file mode 100644 index 0000000000..5e38978584 --- /dev/null +++ b/test/fixtures/order_items.yml @@ -0,0 +1,43 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +pending: + quantity: 1 + status: pending + order: three + product: plushie + +paid: + quantity: 1 + status: paid + order: two + product: puzzle + +shipped: + quantity: 1 + status: shipped + order: one + product: puddy + +cancelled: + quantity: 1 + status: cancelled + order: two + product: plushie + +many: + quantity: 20 + status: pending + order: two + product: puzzle + +paid2: + quantity: 10 + status: paid + order: two + product: puzzle + +pending2: + quantity: 5 + status: pending + order: four + product: yoga diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml new file mode 100644 index 0000000000..321d562cad --- /dev/null +++ b/test/fixtures/orders.yml @@ -0,0 +1,37 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + email: aaa@gmail.com + mailing_address: 12345 A Street + cc: 1234123412341234 + cc_name: Aaa Aaaaaa + cc_expiration: 10/22 + cvv: 123 + zip: 12345 + status: paid + +two: + email: z@zmail.com + mailing_address: 90 Z Ave + cc: 1111222233334444 + cc_name: Zebra Zee + cc_expiration: 12/20 + cvv: 100 + zip: 50100 + status: completed + +three: + email: nil + mailing_address: nil + cc: nil + cc_name: nil + cc_expiration: nil + cvv: nil + zip: nil + status: cart + +four: + status: cart + +five: + status: cart diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml new file mode 100644 index 0000000000..e409de2b01 --- /dev/null +++ b/test/fixtures/products.yml @@ -0,0 +1,38 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +plushie: + name: Brain Plushie + price: 15 + description: A beautiful integlligent brain plushie + inventory: 10 + merchant: one + categories: toy + is_active: true + +puzzle: + name: Brain Puzzle + price: 100 + description: improve ur mind! + merchant: two + inventory: 2 + categories: toy + is_active: true + +puddy: + name: Brain Puddy + price: 14 + description: puddy for ur brain! + inventory: 5 + merchant: two + categories: toy + is_active: true + + +yoga: + name: Yoga mat + price: 475 + description: puddy for ur brain! + inventory: 100 + merchant: three + categories: meditation, apparel + is_active: true diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml new file mode 100644 index 0000000000..79b595d03e --- /dev/null +++ b/test/fixtures/reviews.yml @@ -0,0 +1,16 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + rating: 5 + product: puddy + description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +two: + rating: 3 + product: plushie + description: Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + +three: + rating: 1 + product: plushie + description: Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. diff --git a/test/helpers/.keep b/test/helpers/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/models/category_test.rb b/test/models/category_test.rb new file mode 100644 index 0000000000..5e9af8da53 --- /dev/null +++ b/test/models/category_test.rb @@ -0,0 +1,34 @@ +require "test_helper" + +describe Category do + + describe 'validations' do + before do + @category = Category.new(name: 'candies') + end + + it 'is valid when name is present and unique' do + result = @category.valid? + expect( result ).must_equal true + end + + + it 'is invalid without a name' do + @category.name = nil + + result = @category.valid? + + expect( result ).wont_equal true + expect( @category.errors.messages ).must_include :name + end + + it 'is invalid with a non-unique name' do + @category = Category.new(name: 'toy') + result = @category.valid? + + expect( result ).wont_equal true + expect( @category.errors.messages ).must_include :name + end + + end +end diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb new file mode 100644 index 0000000000..e43134d866 --- /dev/null +++ b/test/models/merchant_test.rb @@ -0,0 +1,75 @@ +require "test_helper" + +describe Merchant do + describe 'relations' do + it 'has many products' do + merchant = Merchant.first + merchant.products.each do |product| + product.must_be_instance_of Product + end + end + end + + + describe 'revenue' do + let(:two) {merchants(:two)} + let(:four) {merchants(:four)} + let(:three) {merchants(:three)} + + it 'calculates revenue for merchant with many products / order items' do + expect(two.revenue).must_equal 1114.00 + end + + it 'calculates revenue for merchant with no products' do + expect(four.revenue).must_equal 0.0 + end + + it 'calculates revenue for merchant with product / no order item ' do + expect(three.revenue).must_equal 0.0 + end + + + end + + describe 'validations' do + before do + @merchant = Merchant.new( + username: 'test_merchant', + email: 'test_merchant@braineaze.com' + ) + end + + it 'is valid when username is present and unique and email is present and unique' do + is_valid = @merchant.valid? + expect( is_valid ).must_equal true + end + + it 'is invalid without a username' do + @merchant.username = nil + is_valid = @merchant.valid? + expect( is_valid ).must_equal false + expect( @merchant.errors.messages ).must_include :username + end + + it 'is invalid without a email' do + @merchant.email = nil + is_valid = @merchant.valid? + expect( is_valid ).must_equal false + expect( @merchant.errors.messages ).must_include :email + end + + it 'is invalid with a non-unique username' do + @merchant.username = Merchant.first.username + is_valid = @merchant.valid? + expect( is_valid ).must_equal false + expect( @merchant.errors.messages ).must_include :username + end + + it 'is invalid with a non-unique email' do + @merchant.email = Merchant.first.email + is_valid = @merchant.valid? + expect( is_valid ).must_equal false + expect( @merchant.errors.messages ).must_include :email + end + end +end diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb new file mode 100644 index 0000000000..a98b906772 --- /dev/null +++ b/test/models/order_item_test.rb @@ -0,0 +1,95 @@ +require "test_helper" + +describe OrderItem do + describe 'relations' do + before do + @order_item = OrderItem.first + end + + it "belongs to a product" do + product = @order_item.product + + expect( product ).must_be_kind_of Product + end + + it "belongs to an order" do + @order_item = OrderItem.first + + order = @order_item.order + + expect( order ).must_be_kind_of Order + end + end + + describe 'validations' do + before do + @order_item = OrderItem.first + end + + it 'is valid with product_id present' do + result = @order_item.valid? + expect( result ).must_equal true + end + + + it 'is invalid without product_id' do + @order_item.product_id = nil + result = @order_item.valid? + + expect( result ).wont_equal true + expect( @order_item.errors.messages ).must_include :product + end + + it 'is valid with order_id present' do + result = @order_item.valid? + + expect( result ).must_equal true + end + + it 'is invalid without order_id' do + @order_item.order_id = nil + result = @order_item.valid? + + expect( result ).wont_equal true + expect( @order_item.errors.messages ).must_include :order + end + + it 'is invalid without status' do + @order_item.status = nil + result = @order_item.valid? + + expect( result ).wont_equal true + expect( @order_item.errors.messages ).must_include :status + end + + it 'is valid with status' do + result = @order_item.valid? + expect( result ).must_equal true + end + + it 'is invalid if quantity is 0' do + @order_item.quantity = 0 + result = @order_item.valid? + + expect( result ).wont_equal true + expect( @order_item.errors.messages ).must_include :quantity + end + + it 'is invalid if quantity is nil' do + @order_item.quantity = nil + result = @order_item.valid? + + expect( result ).wont_equal true + expect( @order_item.errors.messages ).must_include :quantity + end + + # it 'is invalid if quantity is greater than product inventory' do + # @order_item.quantity = 20 + # result = @order_item.valid? + # + # expect( result ).wont_equal true + # expect( @order_item.errors.messages ).must_include :quantity + # end + end + +end diff --git a/test/models/order_test.rb b/test/models/order_test.rb new file mode 100644 index 0000000000..9f4102abad --- /dev/null +++ b/test/models/order_test.rb @@ -0,0 +1,81 @@ +require "test_helper" + +describe Order do + + describe 'relations' do + it "has a list of order items" do + order = orders(:one) + order.must_respond_to :order_items + + order.order_items.each do |order_item| + order_item.must_be_kind_of OrderItem + end + end + end + + describe 'validations' do + before do + @order = orders(:one) + end + + it 'is valid when email, mailing, cc, cc_name, ccv and zip is present' do + is_valid = @order.valid? + expect( is_valid ).must_equal true + end + + it 'is invalid when email is missing' do + @order.email = nil + + is_valid = @order.valid? + + expect( is_valid ).must_equal false + + expect( @order.errors.messages ).must_include :email + end + + it 'is invalid when mailing address is missing' do + @order.mailing_address = nil + + is_valid = @order.valid? + + expect( is_valid ).must_equal false + expect( @order.errors.messages ).must_include :mailing_address + end + + it 'is invalid when cc is missing' do + @order.cc = nil + + is_valid = @order.valid? + + expect( is_valid ).must_equal false + expect( @order.errors.messages ).must_include :cc + end + + it 'is invalid when cc name is missing' do + @order.cc_name = nil + + is_valid = @order.valid? + + expect( is_valid ).must_equal false + expect( @order.errors.messages ).must_include :cc_name + end + + it 'is invalid when cvv name is missing' do + @order.cvv = nil + + is_valid = @order.valid? + + expect( is_valid ).must_equal false + expect( @order.errors.messages ).must_include :cvv + end + + it 'is invalid when zip is missing' do + @order.zip = nil + + is_valid = @order.valid? + + expect( is_valid ).must_equal false + expect( @order.errors.messages ).must_include :zip + end + end +end diff --git a/test/models/product_test.rb b/test/models/product_test.rb new file mode 100644 index 0000000000..7701faa65f --- /dev/null +++ b/test/models/product_test.rb @@ -0,0 +1,110 @@ +require "test_helper" + +describe Product do + describe 'relations' do + it 'has many reviews' do + product = Product.first + product.reviews.each do |review| + review.must_be_instance_of Review + end + end + + it 'has many order_items' do + product = Product.first + product.order_items.each do |order_item| + order_item.must_be_instance_of OrderItem + end + end + + it 'has many categories' do + product = Product.first + product.categories.each do |category| + category.must_be_instance_of Category + end + end + + it 'belongs to a Merchant' do + plushie = products(:plushie) + plushie.must_respond_to :merchant + plushie.merchant.must_be_kind_of Merchant + plushie.merchant.username.must_equal "griffiam" + plushie.merchant.must_equal merchants(:one) + end + end + + describe 'currency' do + it 'converts price to float' do + products(:plushie).currency.must_equal 15.00 + end + end + + describe 'change inventory method' do + it 'changes inventory stock on a product' do + products(:plushie).change_inventory(2) + products(:plushie).inventory.must_equal 8 + end + end + + + describe 'validations' do + before do + @product = Product.new( + name: 'test_product', + price: 1005, + inventory: 4 + ) + end + + it 'is valid when name is present and unique, price is present, and inventory is present and greater or equal to 0' do + plushie = products(:plushie) + plushie.valid?.must_equal true + end + + it 'is invalid without a name' do + @product.name = nil + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :name + end + + it 'is invalid without a price' do + @product.price = nil + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :price + end + + it 'is invalid if price is less or equal to 0' do + @product.price = 0 + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :price + + @product.price = -2 + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :price + end + + it 'is invalid without an inventory amt' do + @product.inventory = nil + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :inventory + end + + it 'is invalid if inventory amt is less than 0' do + @product.inventory = -1 + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :inventory + end + + it 'is invalid with a non-unique name' do + @product.name = Product.first.name + is_valid = @product.valid? + expect( is_valid ).must_equal false + expect( @product.errors.messages ).must_include :name + end + end +end diff --git a/test/models/review_test.rb b/test/models/review_test.rb new file mode 100644 index 0000000000..7ec2105794 --- /dev/null +++ b/test/models/review_test.rb @@ -0,0 +1,72 @@ +require "test_helper" + +describe Review do + describe 'relations' do + it "belongs to a product" do + review = Review.first + product = review.product + product.must_be_instance_of Product + end + end + + describe 'validations' do + before do + @review = Review.new( + product_id: Product.first.id, + rating: 5, + description: 'test-description' + ) + end + + it 'is invalid without a rating' do + @review.rating = nil + result = @review.valid? + + expect( result ).wont_equal true + end + + it 'is invalid if rating is not an integer' do + @review.rating = 'five' + + result = @review.valid? + expect( result ).wont_equal true + end + + it 'is valid when rating is between 1 and 5' do + result = @review.valid? + expect( result ).must_equal true + end + + it 'is invalid when rating is less than 1' do + @review.rating = -1 + result = @review.valid? + + expect( result ).wont_equal true + end + + it 'is invalid when rating is greater than 5' do + @review.rating = 6 + result = @review.valid? + + expect( result ).wont_equal true + end + + it 'is valid with product_id present' do + result = @review.valid? + expect( result ).must_equal true + end + + it 'is valid with a description' do + result = @review.valid? + expect( result ).must_equal true + end + + it 'is invalid without a description' do + @review.description = nil + result = @review.valid? + + expect( result ).wont_equal true + end + + end +end diff --git a/test/system/.keep b/test/system/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000000..d98b637e9a --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,56 @@ +require 'simplecov' + +SimpleCov.start 'rails' do + add_filter '/bin/' + add_filter '/db/' + add_filter '/test/' +end + +ENV["RAILS_ENV"] = "test" +require File.expand_path("../../config/environment", __FILE__) +require "rails/test_help" +require "minitest/rails" +require "minitest/reporters" # for Colorized output + +# For colorful output! +Minitest::Reporters.use!( + Minitest::Reporters::SpecReporter.new, + ENV, + Minitest.backtrace_filter +) + + +# To add Capybara feature tests add `gem "minitest-rails-capybara"` +# to the test group in the Gemfile and uncomment the following: +# require "minitest/rails/capybara" + +# Uncomment for awesome colorful output +# require "minitest/pride" + +class ActiveSupport::TestCase + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + # Add more helper methods to be used by all tests here... + def setup + # Once you have enabled test mode, all requests + # to OmniAuth will be short circuited to use the mock authentication hash. + # A request to /auth/provider will redirect immediately to /auth/provider/callback. + OmniAuth.config.test_mode = true + end + + def mock_auth_hash(user) + return { + uid: user.uid, + provider: user.provider, + info: { + nickname: user.username, + email: user.email + } + } + end + + def login(user) + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new(mock_auth_hash(user)) + get auth_callback_path(:github) + end +end diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 0000000000..e69de29bb2