diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..e3c78a862f --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# 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. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore uploaded files in development +/storage/* +!/storage/.keep + +/node_modules +/yarn-error.log + +/public/assets +.byebug_history + +# Ignore master key for decrypting credentials and more. +/config/master.key +coverage/ +.env \ No newline at end of file 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/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..953e32145c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "workbench.colorCustomizations": { + "tab.unfocusedActiveBorder": "#fff0" + } +} \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..6e63f902df --- /dev/null +++ b/Gemfile @@ -0,0 +1,87 @@ +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +ruby '2.5.1' + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.2.3' +# 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' +# 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 'better_errors' + gem 'binding_of_caller' + gem 'guard' + gem 'guard-minitest' + gem 'dotenv-rails' +end + +group :test do + gem 'minitest-rails' + gem 'minitest-reporters' + gem 'simplecov' + gem 'simplecov-console' +end + +gem "omniauth" +gem "omniauth-github" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..5b27d3a074 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,321 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.2.3) + actionpack (= 5.2.3) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.3) + actionpack (= 5.2.3) + actionview (= 5.2.3) + activejob (= 5.2.3) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.2.3) + actionview (= 5.2.3) + activesupport (= 5.2.3) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.2.3) + activesupport (= 5.2.3) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.2.3) + activesupport (= 5.2.3) + globalid (>= 0.3.6) + activemodel (5.2.3) + activesupport (= 5.2.3) + activerecord (5.2.3) + activemodel (= 5.2.3) + activesupport (= 5.2.3) + arel (>= 9.0) + activestorage (5.2.3) + actionpack (= 5.2.3) + activerecord (= 5.2.3) + marcel (~> 0.3.1) + activesupport (5.2.3) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + ansi (1.5.0) + archive-zip (0.12.0) + io-like (~> 0.3.0) + arel (9.0.0) + autoprefixer-rails (9.5.1) + execjs + better_errors (2.5.1) + coderay (>= 1.0.0) + erubi (>= 1.0.0) + rack (>= 0.9.0) + bindex (0.7.0) + binding_of_caller (0.8.0) + debug_inspector (>= 0.0.1) + bootsnap (1.4.4) + 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 (11.0.1) + capybara (3.18.0) + addressable + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (~> 1.2) + xpath (~> 3.2) + childprocess (1.0.1) + rake (< 13.0) + chromedriver-helper (2.1.1) + archive-zip (~> 0.10) + nokogiri (~> 1.8) + coderay (1.1.2) + concurrent-ruby (1.1.5) + crass (1.0.4) + debug_inspector (0.0.3) + docile (1.3.1) + dotenv (2.7.2) + dotenv-rails (2.7.2) + dotenv (= 2.7.2) + railties (>= 3.2, < 6.1) + erubi (1.8.0) + execjs (2.7.0) + faraday (0.15.4) + multipart-post (>= 1.2, < 3) + ffi (1.10.0) + formatador (0.2.5) + globalid (0.4.2) + activesupport (>= 4.2.0) + guard (2.15.0) + 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.6.0) + hirb (0.7.3) + i18n (1.6.0) + concurrent-ruby (~> 1.0) + io-like (0.3.0) + jbuilder (2.8.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.2.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.3) + 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.2) + mimemagic (0.3.3) + mini_mime (1.0.1) + mini_portile2 (2.4.0) + minitest (5.11.3) + minitest-rails (3.0.0) + minitest (~> 5.8) + railties (~> 5.0) + minitest-reporters (1.3.6) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + msgpack (1.2.10) + 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.10.3) + mini_portile2 (~> 2.4.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.9.0) + hashie (>= 3.4.6, < 3.7.0) + rack (>= 1.6.2, < 3) + omniauth-github (1.3.0) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) + omniauth-oauth2 (1.6.0) + oauth2 (~> 1.1) + omniauth (~> 1.9) + pg (1.1.4) + popper_js (1.14.5) + pry (0.12.2) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry-rails (0.3.9) + pry (>= 0.10.4) + public_suffix (3.0.3) + puma (3.12.1) + rack (2.0.7) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.2.3) + actioncable (= 5.2.3) + actionmailer (= 5.2.3) + actionpack (= 5.2.3) + actionview (= 5.2.3) + activejob (= 5.2.3) + activemodel (= 5.2.3) + activerecord (= 5.2.3) + activestorage (= 5.2.3) + activesupport (= 5.2.3) + bundler (>= 1.3.0) + railties (= 5.2.3) + 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.3) + actionpack (= 5.2.3) + activesupport (= 5.2.3) + method_source + rake (>= 0.8.7) + thor (>= 0.19.0, < 2.0) + rake (12.3.2) + rb-fsevent (0.10.3) + rb-inotify (0.10.0) + ffi (~> 1.0) + regexp_parser (1.4.0) + ruby-progressbar (1.10.0) + ruby_dep (1.5.0) + rubyzip (1.2.2) + sass (3.7.4) + 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.142.0) + childprocess (>= 0.5, < 2.0) + 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-console (0.4.2) + ansi + hirb + simplecov + 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.3) + thread_safe (0.3.6) + tilt (2.0.9) + turbolinks (5.2.0) + turbolinks-source (~> 5.2) + turbolinks-source (5.2.0) + tzinfo (1.2.5) + thread_safe (~> 0.1) + uglifier (4.1.20) + 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) + xpath (3.2.0) + nokogiri (~> 1.8) + +PLATFORMS + ruby + +DEPENDENCIES + 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.3) + sass-rails (~> 5.0) + selenium-webdriver + simplecov + simplecov-console + spring + spring-watcher-listen (~> 2.0.0) + turbolinks (~> 5) + tzinfo-data + uglifier (>= 1.3.0) + web-console (>= 3.3.0) + +RUBY VERSION + ruby 2.5.1p57 + +BUNDLED WITH + 2.0.1 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/README.md b/README.md index 6db044f03d..062cd09a49 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # bEtsy +Dee is doing a weird test right now :) + ## At a Glance - Group, [stage 3](https://github.com/Ada-Developers-Academy/pedagogy/blob/master/rule-of-three.md) project 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/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/.keep b/app/assets/images/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/images/colors.png b/app/assets/images/colors.png new file mode 100644 index 0000000000..310e8868d9 Binary files /dev/null and b/app/assets/images/colors.png differ diff --git a/app/assets/images/doodles.png b/app/assets/images/doodles.png new file mode 100644 index 0000000000..855a89e9e6 Binary files /dev/null and b/app/assets/images/doodles.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 0000000000..27c269c46f --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,19 @@ +// 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/homepages.js b/app/assets/javascripts/homepages.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/homepages.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/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.js b/app/assets/javascripts/order.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/order.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/orderitems.js b/app/assets/javascripts/orderitems.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/app/assets/javascripts/orderitems.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/about.scss b/app/assets/stylesheets/about.scss new file mode 100644 index 0000000000..181769510c --- /dev/null +++ b/app/assets/stylesheets/about.scss @@ -0,0 +1,10 @@ +.main-content { + margin: 100px 0px 50px 0px; + z-index: 100; +} + + #left-text, #left-about { + text-align: left; +} + + diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 0000000000..e63c5728f2 --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,224 @@ + +@import "bootstrap"; +/* Import scss content */ +@import "**/*"; +@import url('https://fonts.googleapis.com/css?family=Nunito'); + +html{ + height:100%; +} + +:root { + --blue: #cceedd; + --indigo: #8855ff; + --purple: #cc66ee; + --pink: #ffaaaa; + --red: #dc3545; + --orange: #ff8754; + --yellow: #ffc107; + --green: #aacc44; + --teal: #88ccaa; + --cyan: #cceedd; + --white: #fff; + --gray: #99aabb; + --gray-dark: #343a40; + --primary: #88ccaa; + --secondary: #8855ff; + --success: #aacc44; + --info: #88ccaa; + --warning: #ffc107; + --danger: #ff8754; + --light: #cceedd; + --dark: #343a40; +} + +body { + // background-image: url("doodles.png"); + font-family: 'Nunito', sans-serif; + display: block; + margin-left: auto; + margin-right: auto; + // max-width: 1088px; +} + +strong { + color: #15cba6; + font-weight: bold; +} + +footer { + color: #099a97; + text-align: center; +} + +main{ + min-height: 1500px; + border-radius: 0px 0px 10px 10px; + padding-left: 5%; + padding-right: 5%; + padding-top: 2px; +} + +li { + list-style: none; +} + +.header { + width: 100%; + position: fixed; + padding-bottom: 10px; + left: 1px; + right: 2px; + background-color: white; + border-bottom: solid; + border-bottom-color: #c9c9c9; + border-width: thin; + z-index: 400; +} + +.logo { + display: inline-block; + padding-left: 20px; +} + +.logo a:visited { + color: #099a97; +} + +.logo a:hover { + color: #7a0abb; +} + + +.motto { + text-align: center; + font-size: .8rem; + color: rgb(117, 6, 90); + letter-spacing: .2em; + font-family: 'Itim', sans-serif; +} + +.navigation ul { + padding-inline-start: 5px; + margin-bottom: 0; + align-items: center; +} + +.nav-app-header { + display: flex; + flex-direction: row; + font-family: 'Itim', sans-serif; + margin-left: 110px; + margin-right: 110px; + padding-bottom: 20px; + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; + padding: 0.5rem 1rem; +} + +.app-header__nav_item { + display: inline-block; + padding-left: 10px; + +} + +.main-container { + display: flex; + flex-direction: column; +} + +a:visited { + color: #292F36; + +} +a:link { + color: #292F36; +} + +a:hover { + color: #15cba6; + text-decoration: none; +} + +#session { + background-color: #FFFFFF; +} + +#session:hover { + color: #099a97; + border: #099a97 solid 1px; +} + +.btn-primary { + background-color: white; + color: var(--primary); + border: 1px #292F36 solid; +} + +.btn-primary:hover { + color: var(--primary); + background-color: #099a97; + border: 1px #099a97 solid; +} + +.btn-primary:visited { + color: var(--primary); + border: 1px #292F36 solid; +} + +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown-content { + display: none; + position: absolute; + background-color: #fff; + text-align: center; + min-width: 10em; + box-shadow: 0px 5px 5px 0px rgba(0,0,0,0.2); + z-index: 1; + font-weight: bolder; + white-space: nowrap; +} + +.dropdown:hover .dropdown-content, +.dropdown:hover .dropdown-content li { + display: block; + width: 100%; +} + +.dropdown .dropdown-content:hover li:hover { + background-color: #ddd; + width: 100%; +} + +.pull-right { + position: relative; + margin-left: auto; + padding-bottom: 10px; +} + +/* Footer */ + +#itsy-foot { + background-color: #95d9b3 ; + margin-top: 103px; + height: 220px; + font-size: 15px; +} + + +.text-uppercase, .copyright { + color: white; +} + +#itsy-foot p { + color: #ffffff; + font-size: 17px; +} + diff --git a/app/assets/stylesheets/categories.scss b/app/assets/stylesheets/categories.scss new file mode 100644 index 0000000000..181c95946f --- /dev/null +++ b/app/assets/stylesheets/categories.scss @@ -0,0 +1,68 @@ +// 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/ + +// main { +// min-height: 500px; +// padding: 10%; +// margin-top: -5px; +// border-radius: 0px 0px 10px 10px; +// } + +.list{ + display: inline; + +} + +.category-heading{ + padding-top: 6%; + padding-bottom: 3%; +} +.category-container { + display: block; + flex-direction: column; + text-align: center; + padding-top: 10% +} + +.category-img { + width: 60%; + height: auto; + vertical-align: middle;; +} + +.category-link { + font-size: 20pt; +} + +.list-type4{ + width:410px; + margin:0 auto; +} + +.list-type4 ul{ + counter-reset: li; + list-style: none; + *list-style: decimal; + font-size: 15px; + padding: 0; + margin-bottom: 4em; +} + +.list-type4 a{ + position: relative; + display: block; + // padding: .4em .4em .4em 2em; + *padding: .4em; + margin: .5em 0; + background: #de99f5; + color: white; + text-decoration: none; + box-shadow:inset 0.5em 0 black; + -webkit-transition: box-shadow 1s; /* For Safari 3.1 to 6.0 */ + transition: box-shadow 1s; +} + +.list-type4 a:hover{ + box-shadow:inset 2em 0 black; +} \ No newline at end of file diff --git a/app/assets/stylesheets/homepages.scss b/app/assets/stylesheets/homepages.scss new file mode 100644 index 0000000000..2305c36d10 --- /dev/null +++ b/app/assets/stylesheets/homepages.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Homepages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/merchants.scss b/app/assets/stylesheets/merchants.scss new file mode 100644 index 0000000000..98792274fe --- /dev/null +++ b/app/assets/stylesheets/merchants.scss @@ -0,0 +1,104 @@ +// 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/ +section { + padding: 60px 0; +} + +div h1.section-title { + text-align: center; + color: #007b5e; + margin-bottom: 50px; + text-transform: uppercase; +} + +#tabs { +background-color:transparent; + color: #333; +} + +#tabs h6.section-title { + color: #eee; +} + +#tabs .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { + color: #333; + background-color: transparent; +border-color: transparent transparent #f3f3f3; + border-bottom: 4px solid !important; + font-size: 1.5em; + font-weight: bold; +} + +#tabs .nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem; + color: #333; + font-size: 1.5em; +} + + +/* Merchant Index */ + +.all-merchants-head { + padding-top: 5%; + padding-bottom: 3%; + text-align: center; +} + +.merch-list-container { + padding-bottom: 3%; +// text-decoration: underline; + text-align: center; +} + +/* Merchant Show */ + +.img-responsive { + width:100%; +} + +.table img { + max-width: 30%; +} + +.table { + padding-left: 5%; + width: 100%; +} + +.merch-container { + padding: 5%; + /* width: 500px; */ + height: 700px; + align-items: center; +} + +.username-heading { + text-align: center; +} + +.merch-header { + padding-top: 5%; + background:#c3e9d6; +} + +.merch-header { + padding-left: 5%; + padding-top: 3%; + padding-bottom: 3%; + margin-bottom: 3%; + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); + color: white; + text-shadow: 2px 2px #77be9b; +} + +.dashboard-title { + padding-top: 5%; + text-align: center; +} + +.merchant-content { + padding: 5%; +} \ No newline at end of file diff --git a/app/assets/stylesheets/order.scss b/app/assets/stylesheets/order.scss new file mode 100644 index 0000000000..3d54d9a374 --- /dev/null +++ b/app/assets/stylesheets/order.scss @@ -0,0 +1,48 @@ +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ +/* Filled cart */ + +.cart-heading { + padding-top: 10%; + text-align: center; + } + + .cart-cost { + padding-right: 10%; + padding-top: 10px; + padding-bottom: 5px; + background: #dfeee6; + text-align: right; + } + + .cart-cost-divider { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #88ccaa;; + margin: 1em 0; padding: 0; + } + + .total-cost { + font-weight: bold; + } + + .checkout-btn { + padding-left: 75%; + } + /* Empty Cart */ + .empty-head { + padding-top: 5%; + + .button-container { + text-align: center; + } + } + + /* Checkout page */ + + .checkout-heading { + padding-top: 10%; + text-align: center; + } + \ No newline at end of file diff --git a/app/assets/stylesheets/orderitems.scss b/app/assets/stylesheets/orderitems.scss new file mode 100644 index 0000000000..651ea3f922 --- /dev/null +++ b/app/assets/stylesheets/orderitems.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Orderitems controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/orders.scss b/app/assets/stylesheets/orders.scss new file mode 100644 index 0000000000..36a5020be4 --- /dev/null +++ b/app/assets/stylesheets/orders.scss @@ -0,0 +1,57 @@ +// 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/ + +/* Filled cart */ + +.cart-heading { + padding-top: 10%; + text-align: center; +} + +.cart-cost { + padding-right: 10%; + padding-top: 10px; + padding-bottom: 5px; + background: #dfeee6; + text-align: right; +} + +.cart-cost-divider { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #88ccaa;; + margin: 1em 0; padding: 0; +} + +.total-cost { + font-weight: bold; +} + +.checkout-btn { + padding-left: 75%; +} +/* Empty Cart */ +.empty-head { + padding-top: 5%; + text-align: center; +} + +.banner { + display: flex; + flex-direction: column; + width: 100%; +} + +.button-container { + text-align: center; +} + +/* Checkout page */ + +.checkout-heading { + padding-top: 10%; + text-align: center; +} + diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss new file mode 100644 index 0000000000..27e8131aa3 --- /dev/null +++ b/app/assets/stylesheets/products.scss @@ -0,0 +1,18 @@ +// 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/ + +.card-columns { + padding-top: 10%; + z-index: -1; + position: relative; +} + +.product-show-img { + max-width: 70%; +} + +.product-main-article { + padding: 3%; + padding-top: 12%; +} \ No newline at end of file diff --git a/app/assets/stylesheets/reviews.scss b/app/assets/stylesheets/reviews.scss new file mode 100644 index 0000000000..41285ad069 --- /dev/null +++ b/app/assets/stylesheets/reviews.scss @@ -0,0 +1,8 @@ +// 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/ + + +.review-main { + padding-top: 10%; +} \ No newline at end of file diff --git a/app/assets/stylesheets/root.scss b/app/assets/stylesheets/root.scss new file mode 100644 index 0000000000..9e9a6ff992 --- /dev/null +++ b/app/assets/stylesheets/root.scss @@ -0,0 +1,145 @@ +section { + padding-bottom: 0px; +} + +hr { + border: 0; + clear:both; + display:block; + width: 96%; + background-color: gainsboro; + height: 1px; +} + +/* Carousel */ + +#carousel { + display: block !important; + z-index: -1; + max-width: 100%; +} + +.w-100 { + width: 100% !important; +} + +.container { + /* width: 500px; */ + /* height: 700px; */ + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: center; +} + +/* Featured Section */ + +#featured-section { + display: flex; + flex-direction: row; +} + +.product-name { + font-size: 20px; + font-weight: bold; + color: #099a97; +} + +.featured-products { + display: flex; + justify-content: space-evenly; + padding-right: 7rem; + padding-left: 7rem; + padding-bottom: 4rem; +} + +.featured-img { + margin-right: 10px; + margin-left: 10px; + padding-right: 10px; + padding-left: 10px; + border: solid; + border-color: #95d9b3; + border-width: 1px; + border-radius: 6px; + padding-top: 10px; + padding-bottom: 10px; +} + + +.container p { + padding-top: 10px; + text-align: center; +} + +.featured-merchant { + margin: -20px; + font-size: 10pt; +} + +/* Slogan Box */ + +.slogan-box { + background-color: #e2f5ea ; + display: flex; + justify-content: space-evenly; + padding-right: 7rem; + padding-left: 7rem; + padding-top: 4rem; + padding-bottom: 4rem; +} + +.slogan-box img { + object-fit: cover; + max-width: 500px; + max-height: 500px; +} + +h2.whats-itsy { + border-bottom: 2px solid #000; + padding-bottom: 3px; +} + +div.about-itsy { + display: flex; + flex-direction: column; + padding-left: 3rem; + align-items: center; + justify-content: center; + text-align: center; +} + +/* Featured Categories */ + +.featured-cat-title { + padding-bottom: 8px; + text-align: center; +} +.featured-categories { + display: inline-flex; + flex-direction: row; + justify-content: space-evenly; + padding-right: 7rem; + padding-left: 7rem; + padding-top: 4rem; + padding-bottom: 4rem; +} + +.feat-cat { + width: 100%; + height: 80%; + // margin-right: 10px; + // margin-left: 10px; + padding-right: 20px; + padding-left: 20px; +} + +.btn-primary { + color: white; + border: 1px #292F36 solid; +} + +div.card-deck { + z-index: -1; +} + 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..322ee1d4c3 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,16 @@ +class ApplicationController < ActionController::Base + before_action :find_merchant + + def find_merchant + @current_merchant = Merchant.find_by(id: session[:user_id]) + end + + def require_login + current_merchant = find_merchant + if current_merchant.nil? + flash[:status] = :error + flash[:result_text] = "You must be logged in to perform this action." + redirect_to root_path + end + end +end diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000000..3dd650e939 --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,55 @@ +class CategoriesController < ApplicationController + before_action :require_login, only: [:new, :create] + + def index + @categories = Category.all + end + + def show + @category = Category.find_by(id: params[:id]) + + if !@category + flash[:status] = :warning + flash[:result_text] = "A problem occurred: Media not found" + redirect_to root_path + end + end + + def new + @category = Category.new + end + + def create + @category = Category.new(category_params) + if @category.save + flash[:status] = :success + flash[:result_text] = "Successfully created #{@category.name} category" + redirect_to merchant_path(session[:user_id]) + else + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Could not add category" + @category.errors.messages.each do |field, messages| + flash.now[field] = messages + end + render :new, status: :bad_request + end + end + + private + + def current_user + @current_user ||= Merchant.find(session[:user_id]) if session[:user_id] + end + + def require_login + if current_user.nil? + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: You must login to view this page" + redirect_to root_path + end + end + + def category_params + return 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/homepages_controller.rb b/app/controllers/homepages_controller.rb new file mode 100644 index 0000000000..647748af42 --- /dev/null +++ b/app/controllers/homepages_controller.rb @@ -0,0 +1,7 @@ +class HomepagesController < ApplicationController + def root + end + + def about + end +end diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb new file mode 100644 index 0000000000..a9b9c42465 --- /dev/null +++ b/app/controllers/merchants_controller.rb @@ -0,0 +1,30 @@ +class MerchantsController < ApplicationController + before_action :find_merchant, only: [:show, :dashboard] + + def index + @merchants = Merchant.all + end + + def show + end + + def dashboard + @merchant = Merchant.find_by(id: session[:user_id]) + @orderitems = @merchant.items_by_status("all") + @pendingitems = Merchant.items_by_orderid(@orderitems["pending"]) + @paiditems = Merchant.items_by_orderid(@orderitems["paid"]) + @completeitems = Merchant.items_by_orderid(@orderitems["complete"]) + @cancelleditems = Merchant.items_by_orderid(@orderitems["cancelled"]) + @products = @merchant.products + @activeproducts = @products.order(:name).where(active: true).where("stock > ?", 0) + @soldout = @products.order(:name).where(active: true).where(stock: 0) + @inactive = @products.order(:name).where(active: false) + end + + private + + def find_merchant + @merchant = Merchant.find_by(id: params[:id]) + head :not_found unless @merchant + end +end diff --git a/app/controllers/orderitems_controller.rb b/app/controllers/orderitems_controller.rb new file mode 100644 index 0000000000..7da68f9f75 --- /dev/null +++ b/app/controllers/orderitems_controller.rb @@ -0,0 +1,101 @@ +class OrderitemsController < ApplicationController + def create + # raise + @product = Product.find_by(id: params[:product_id]) + + if @product + @orderitem = Orderitem.new(orderitem_params) + # not actually changing stock, just using this as a device to generate errors + @product.stock -= params[:orderitem][:quantity].to_i + + if !@product.valid? + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: not enough available stock" + redirect_to product_path(@product.id) + return + end + + if !session[:order_id] + @order = Order.create + session[:order_id] = @order.id + end + @orderitem.product_id = @product.id + @orderitem.order_id = session[:order_id] + is_successful = @orderitem.save + + if is_successful + flash[:status] = :success + flash[:result_text] = "Succesfully added an itsy item to your cart" + redirect_to product_path(@product.id) + else + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Could not add item to cart" + @orderitem.errors.messages.each do |field, messages| + flash.now[field] = messages + end + redirect_to product_path(@product.id) + end + else + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Can't find product" + redirect_to products_path + end + end + + def update + @orderitem = Orderitem.find_by(id: params[:id]) + if !@orderitem + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Could not find item" + redirect_to root_path + else + @product = @orderitem.product + # not actually changing stock, just using this as a device to generate errors + @product.stock -= params[:quantity].to_i + + if !@product.valid? + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: not enough available stock" + redirect_to order_path(@orderitem.order.id) + return + end + + @orderitem.quantity = params[:quantity].to_i + if @orderitem.save + flash[:status] = :success + flash[:result_text] = "Successfully updated item: #{@orderitem.product.name}" + redirect_to order_path(@orderitem.order.id) + else + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Could not update item" + @orderitem.errors.messages.each do |field, messages| + flash.now[field] = messages + end + redirect_to order_path(@orderitem.order.id) + end + end + end + + def destroy + orderitem = Orderitem.find_by(id: params[:id]) + if orderitem.nil? + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Could not find item" + if !session[:order_id] + redirect_to products_path + return + end + else + orderitem.destroy + flash[:status] = :success + flash[:result_text] = "Succesfully deleted item!" + end + redirect_to order_path(session[:order_id]) + end + + private + + def orderitem_params + return params.require(:orderitem).permit(:quantity) + end +end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb new file mode 100644 index 0000000000..a08047cdc6 --- /dev/null +++ b/app/controllers/orders_controller.rb @@ -0,0 +1,114 @@ +class OrdersController < ApplicationController + before_action :find_order, only: [:edit, :update, :show, :review, :confirmation] + + def index + @orders = Order.all + end + + def update + if @order.nil? + redirect_to products_path + else + @order.update(status: Order::PENDING) + is_successful = @order.update(order_params) + if is_successful + redirect_to review_order_path(@order.id) + else + @order.errors.messages.each do |field, message| + flash.now[field] = message + end + + render :edit, status: :bad_request + end + end + end + + def show + if @order.nil? + flash[:status] = :error + flash[:result_text] = "Order not found!" + redirect_to orders_path + elsif @order.orderitems + @order.orderitems.each do |item| + orig_quantity = item.quantity + new_quantity = item.adjust_quantity + no_stock = true if new_quantity == 0 + if orig_quantity != new_quantity + # had to write this in a weird way so that the test would pass + if flash.now[:status] != :warning + flash.now[:status] = :warning + flash.now[:result_text] = "Some item quantities in your cart have changed due to availability: #{item.product.name}" + else + flash.now[:result_text] << ", #{item.product.name}" + end + item.destroy if no_stock + end + end + end + end + + def review + end + + def confirmation + if @order.nil? + redirect_to root_path + else + @order.update(status: Order::PAID) + session[:order_id] = nil + # @temp_orderitems = [] + # @order.orderitems.each do |item| + # hash = { + # orderitem_id: item.id, + # product_name: item.product.name, + # product_price: item.product.price, + # quantity: item.quantity, + # item_total: item.quantity * item.product.price, + # } + + # @temp_orderitems << hash + # end + end + end + + def cancel + @order = Order.find_by(id: params[:id]) + + if @order.nil? + flash[:status] = :error + flash[:result_text] = "Order does not exist." + redirect_to root_path + else + is_succesfull = @order.update(status: Order::CANCELLED) + session[:order_id] = nil + flash[:status] = :success + flash[:result_text] = "Your order has been cancelled." + + redirect_to root_path + end + end + + def shipped + @order = Order.find_by(id: params[:id]) + + if @order + @order.update(status: Order::COMPLETE) + flash[:status] = :success + flash[:result_text] = "Little order ##{@order.id} marked shipped!" + else + flash[:status] = :warning + flash[:result_text] = "An itsy problem occurred: Could not find order" + end + redirect_to dashboard_path(session[:user_id]) + end + + private + + def find_order + @order = Order.find_by(id: session[:order_id]) + end + + def order_params + return params.require(:order).permit(:address, :name, :status, :cc, :expiration_date, :email, :csv) + end +end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb new file mode 100644 index 0000000000..20630accbe --- /dev/null +++ b/app/controllers/products_controller.rb @@ -0,0 +1,99 @@ +class ProductsController < ApplicationController + before_action :find_product, only: [:show, :edit, :update, :status, :retire] + before_action :find_merchant, only: [:new, :edit, :create] + before_action :require_login, only: [:new, :edit, :update, :create, :retire] + + def index + @products = Product.active_products + end + + def show + @orderitem = Orderitem.new + @review = Review.new + @reviews = @product.reviews if @product + if @product.nil? || @product.active == false + flash[:status] = :warning + flash[:result_text] = "#{@product.name} is not active." + redirect_to dashboard_path(session[:user_id]) + end + # if @product.nil? + # flash[:status] = :error + # flash[:result_text] = "Product does not exist." + # redirect_to dashboard_path + # elsif @product.active == false + # flash[:status] = :warning + # flash[:result_text] = "#{@product.name} is not active." + # redirect_to dashboard_path + # else + # @reviews = @product.reviews + # end + end + + def retire + @product.active = false if @product + if @product.save + flash[:status] = :success + flash[:result_text] = "#{@product.name} has been retired." + redirect_to dashboard_path(session[:user_id]) + end + end + + def new + @product = Product.new + end + + def edit + if @product.nil? + flash[:status] = :error + flash[:result_text] = "Product not found." + redirect_to dashboard_path(session[:user_id]) + end + end + + def create + params = product_params + params[:merchant_id] = session[:user_id] + params[:photo_url] = "http://placekitten.com/200/300" if params[:photo_url].nil? + + @product = Product.new(params) + + if @product.save + flash[:status] = :success + flash[:result_text] = "Successfully created #{@product.name}" + redirect_to product_path(@product) + else + flash[:status] = :error + flash[:result_text] = "Experiencing an issue with creating the product." + flash[:messages] = @product.errors.messages + render :new, status: :bad_request + end + end + + def update + if @product.update(product_params) + merchant_id = session[:user_id] + flash[:status] = :success + flash[:result_text] = "Successfully updated #{@product.name}" + redirect_to product_path(@product.id) + else + flash[:status] = :error + flash[:result_text] = "Failed to update" + flash[:messages] = @product.errors.messages + render :edit, status: :bad_request + end + end + + private + + def find_product + @product = Product.find_by(id: params[:id].to_i) + + if @product.nil? + flash.now[:warning] = "Cannot find the product #{params[:id]}" + end + end + + def product_params + params.require(:product).permit(:name, :price, :description, :photo_url, :active, :merchant_id, :stock, category_ids: []) + end +end diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb new file mode 100644 index 0000000000..220c2d4e7e --- /dev/null +++ b/app/controllers/reviews_controller.rb @@ -0,0 +1,40 @@ +class ReviewsController < ApplicationController + def new + @review = Review.new + end + + def create + @product = Product.find_by(id: params[:product_id]) + @review = Review.new(review_params) + + if @product + if @product.merchant_id == session[:user_id] + flash[:status] = :error + flash[:result_text] = "An itsy problem occurred: You can't rate your own products" + redirect_to dashboard_path(session[:user_id]) + return + end + + if @product.reviews << @review + flash[:status] = :success + flash[:result_text] = "Thank you for your feedback" + redirect_to product_path(params[:product_id]) + else + flash[:status] = :error + flash[:result_text] = "An itsy problem occurred: Could not process feedback" + @review.errors.messages.each do |field, messages| + flash.now[field] = messages + end + render :new, status: :bad_request + end + else + flash[:status] = :error + flash[:result_text] = "An itsy problem occurred: Product not found" + redirect_to products_path + end + end + + def review_params + return params.require(:review).permit(:rating, :body) + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000000..02379a2663 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,29 @@ +class SessionsController < ApplicationController + def create + auth_hash = request.env["omniauth.auth"] + + merchant = Merchant.find_by(uid: auth_hash[:uid], provider: "github") + + if merchant + flash[:success] = "Welcome #{merchant.username}" + else + merchant = Merchant.build_from_github(auth_hash) + + if merchant.save + flash[:success] = "Logged in as #{merchant.username}" + else + flash[:error] = "Could not log in with account #{merchant.errors.messages}" + return redirect_to root_path + end + end + + session[:user_id] = merchant.id + return redirect_to root_path + end + + def destroy + session[:user_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..21dde82cff --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,36 @@ +module ApplicationHelper + def display_price(price) + ("" + "#{number_to_currency(price)}" + "").html_safe + end + + def display_date(date) + ("" + "#{date.strftime("%B %d, %Y")} at #{date.strftime("%H:%M %p")}" + "").html_safe + end + + def cc_display(order) + cc = order.cc_num.gsub(/\D/, '') + last = cc.size - 1 + first = cc.size - 4 + return cc[first..last] + end + + def shopping_cart_count(count) + ''.html_safe + "#{count}" ''.html_safe + end + + def orderitems_total_quantity(orderitems) + if orderitems.nil? + return 0 + else + return orderitems.sum {|oi| oi.quantity } + end + end + + def display_order(order) + if order == 1 + return "Order" + else + return "Orders" + end + end +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/homepages_helper.rb b/app/helpers/homepages_helper.rb new file mode 100644 index 0000000000..4bd8098f37 --- /dev/null +++ b/app/helpers/homepages_helper.rb @@ -0,0 +1,2 @@ +module HomepagesHelper +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_helper.rb b/app/helpers/order_helper.rb new file mode 100644 index 0000000000..05d197a0c1 --- /dev/null +++ b/app/helpers/order_helper.rb @@ -0,0 +1,2 @@ +module OrderHelper +end diff --git a/app/helpers/orderitems_helper.rb b/app/helpers/orderitems_helper.rb new file mode 100644 index 0000000000..c74e407d1c --- /dev/null +++ b/app/helpers/orderitems_helper.rb @@ -0,0 +1,2 @@ +module OrderitemsHelper +end diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb new file mode 100644 index 0000000000..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..6ebaae575b --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,6 @@ +class Category < ApplicationRecord + has_and_belongs_to_many :products + + # unique + 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..104663c623 --- /dev/null +++ b/app/models/merchant.rb @@ -0,0 +1,90 @@ +class Merchant < ApplicationRecord + STATUSES = %w(Pending Paid Complete Cancelled) + + has_many :products + has_many :orderitems, through: :products + validates :username, presence: true, uniqueness: true + validates :email, presence: true, uniqueness: true + + def self.build_from_github(auth_hash) + return Merchant.new(uid: auth_hash[:uid], + provider: "github", + email: auth_hash["info"]["email"], + username: auth_hash["info"]["name"]) + end + + def items_by_status(status) + items = self.orderitems.group_by { |oid| oid.order.status } + + available_statuses = items.keys + + if status == "all" + return items + else + if STATUSES.include?(status) && available_statuses.include?(status) + return items[status] + # orderitem[status] + else + return [] + end + end + end + + def self.items_by_orderid(items) + if items.nil? || items.empty? + return [] + end + items = items.group_by { |oi| oi.order.id } + return items + end + + def total_revenue + paid_items = self.items_by_status("Paid") + complete_items = self.items_by_status("Complete") + + if paid_items.nil? + revenue_paid = 0 + else + revenue_paid = paid_items.sum { |oi| oi.total_price } + end + + if complete_items.nil? + revenue_complete = 0 + else + revenue_complete = complete_items.sum { |oi| oi.total_price } + end + + return revenue_paid + revenue_complete + end + + def revenue_by_status + revenue = Hash.new + paid_items = self.items_by_status("Paid") + complete_items = self.items_by_status("Complete") + + if paid_items.nil? + revenue["paid"] = 0 + else + revenue["paid"] = paid_items.sum { |oi| oi.total_price } + end + + if complete_items.nil? + revenue["complete"] = 0 + else + revenue["complete"] = complete_items.sum { |oi| oi.total_price } + end + + return revenue + end + + def orders_by_status(status) + orders = [] + + self.orderitems.each do |item| + if !orders.include?(Order.where(id: item.order.id)) && item.order.status == status + orders << item.order + end + return orders + end + end +end diff --git a/app/models/order.rb b/app/models/order.rb new file mode 100644 index 0000000000..4548db42a9 --- /dev/null +++ b/app/models/order.rb @@ -0,0 +1,33 @@ +class Order < ApplicationRecord + has_many :orderitems + # validates :name, presence: true, allow_nil: true, if: :status_nil? + validates :name, presence: true, allow_nil: false, unless: :status_nil? + # validates :email, presence: true, allow_nil: true, if: :status_nil? + validates :email, presence: true, allow_nil: false, unless: :status_nil? + validates :address, presence: true, allow_nil: false, unless: :status_nil? + validates :cc, presence: true, allow_nil: false, unless: :status_nil? + validates :csv, presence: true, allow_nil: false, unless: :status_nil? + validates :expiration_date, presence: true, allow_nil: false, unless: :status_nil? + + PENDING = "Pending" + PAID = "Paid" + COMPLETE = "Complete" + CANCELLED = "Cancelled" + SHIPPED = "Shipped" + + def status_nil? + return true if self.status == nil + end + + def sub_total + return self.orderitems.map.sum { |item| item.product.price * item.quantity } + end + + def tax + return sub_total * 0.09 + end + + def total + return tax + sub_total + end +end diff --git a/app/models/orderitem.rb b/app/models/orderitem.rb new file mode 100644 index 0000000000..ba0b8f4a8d --- /dev/null +++ b/app/models/orderitem.rb @@ -0,0 +1,24 @@ +class Orderitem < ApplicationRecord + belongs_to :order + belongs_to :product + + validates :quantity, presence: true, numericality: { greater_than: 0 } + def self.status + return self.order.status + end + + def adjust_quantity + product = self.product + if product.stock < self.quantity + self.quantity = product.stock + self.save + return product.stock + else + return self.quantity + end + end + + def total_price + return self.quantity * self.product.price + end +end diff --git a/app/models/product.rb b/app/models/product.rb new file mode 100644 index 0000000000..3530ab4dae --- /dev/null +++ b/app/models/product.rb @@ -0,0 +1,30 @@ +class Product < ApplicationRecord + has_and_belongs_to_many :categories + has_many :reviews + belongs_to :merchant + has_many :orderitems + has_many :orders, through: :orderitems + + validates :name, presence: true, uniqueness: { scope: :merchant, message: "Merchant's products must have unique names " } + validates :price, presence: true, numericality: { greater_than: 0, message: "Price must be greater than 0" } + validates :stock, presence: true, numericality: { greater_than_or_equal_to: 0, message: "Stock cannot go below 0" } + + def self.active_products + return Product.where(active: :true) + end + + def average_rating + sum = 0 + count = self.reviews.count + + if count == 0 + return 0 + else + self.reviews.each do |review| + sum += review.rating + end + + return (sum * 1.0 / count).round(2) + end + end +end diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 0000000000..4723efe3db --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,8 @@ +class Review < ApplicationRecord + belongs_to :product + + validates :rating, presence: true, + :inclusion => { :in => 1..5, + :message => "must be a number between 1 and 5" } + validates :body, presence: true +end diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb new file mode 100644 index 0000000000..0986f687b9 --- /dev/null +++ b/app/views/categories/index.html.erb @@ -0,0 +1,12 @@ +
+
+

Itsy Categories

+ <% @categories.each do |category| %> + + <% end %> +
+
diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb new file mode 100644 index 0000000000..22eed89e8f --- /dev/null +++ b/app/views/categories/new.html.erb @@ -0,0 +1,13 @@ +
+

Create a new category

+ + <%= form_with model: @category, url: categories_path do |f|%> + +
+ <%= f.label :new_category %> + <%= f.text_field :name, class: "form-control" %> +
+ + <%= f.submit 'Create category', class: "btn btn-primary" %> + <% end %> +
diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb new file mode 100644 index 0000000000..872d765ce8 --- /dev/null +++ b/app/views/categories/show.html.erb @@ -0,0 +1,24 @@ + +
+
+

<%= @category.name %>

+ + + + + + + + + + <% @category.products.each do |product| %> + + + + + + <% end %> + +
PhotoNamePrice
Image of <%=product.name%><%= link_to product.name, product_path(product.id) %><%= product.price %>
+
+
\ No newline at end of file diff --git a/app/views/homepages/about.html.erb b/app/views/homepages/about.html.erb new file mode 100644 index 0000000000..39d163408b --- /dev/null +++ b/app/views/homepages/about.html.erb @@ -0,0 +1,84 @@ +
+
+

About

+

Itsy is the global marketplace for all things SMALL. It’s home to a + universe of perfectly small, extraordinary items, from unique handcrafted pieces to vintage treasures. + Born from a shared passion between friends, Itsy is now that #1 retail destination in the + world! Mini is the new big, and we're proud to be at the forefront. +

+

Meet the Team Behind Itsy

+
+
+
+
+ <%= image_tag("https://i.ibb.co/DGssmCq/kate.png", alt: "Avatar for Kate Sandler", class: "card-img-top") %> +
+
+
+
Kate Sandler
+

Kate Sandler is fueled by her passion for understanding the nuances of all things + tiny. She considers herself a ‘mini die-hard,’ eager to both build on her + academic foundations in itsyology and stay in tune with the latest + miniature marketing strategies through continued coursework and professional development.

+

Itsy Engineer

+
+
+
+
+ +
+
+
+ <%= image_tag("https://i.ibb.co/fd6w6JV/kim.png", alt: "Avatar of Kim", class: "card-img-top") %> +
+
+
+
Kim Fasbender
+

Kim Fasbender is fueled by her passion for understanding the nuances of all things + tiny. She considers herself a ‘mini die-hard,’ eager to both build on her + academic foundations in itsyology and stay in tune with the latest + miniature marketing strategies through continued coursework and professional development.

+

Itsy Engineer

+
+
+
+
+ + +
+
+
+ <%= image_tag("https://i.ibb.co/DpvPBD2/sopheary.png", alt: "Avatar of Sopheary", class: "card-img-top") %> +
+
+
+
Sopheary Chiv
+

Sopheary Chiv is fueled by her passion for understanding the nuances of all things + tiny. She considers herself a ‘mini die-hard,’ eager to both build on her + academic foundations in itsyology and stay in tune with the latest + miniature marketing strategies through continued coursework and professional development.

+

Itsy Engineer

+
+
+
+
+ +
+
+
+ <%= image_tag("https://i.ibb.co/qjByvfT/steph.png", alt: "Avatar of Stephanie", class: "card-img-top") %> +
+
+
+
Stephanie Marchante
+

Stephanie Marchante is fueled by her passion for understanding the nuances of all things + tiny. She considers herself a ‘mini die-hard,’ eager to both build on her + academic foundations in itsyology and stay in tune with the latest + miniature marketing strategies through continued coursework and professional development.

+

Itsy Engineer

+
+
+
+
+
+
diff --git a/app/views/homepages/root.html.erb b/app/views/homepages/root.html.erb new file mode 100644 index 0000000000..87a544910c --- /dev/null +++ b/app/views/homepages/root.html.erb @@ -0,0 +1,120 @@ + + +
+
+
+ +
+

Featured Products

+
+
+ + itty bitty rainbow pancakes +

Scanty Rainbow Pancakes

+
+ + + +
+
+ + very tiny pink iPad +

Itty Bitty iPad

+
+ + + +
+
+ + small pink cake +

Pocket-Sized Pink Cake

+
+ + + +
+ +
+ + tiny burger on a fork +

Bitty Burger

+
+ + + +
+
+
+ +
+
+
+
+ +
+ itsy slogan +
+

Our Mini Motive

+

It's a big world, but it's the tiny things that matter. We're passionate about the simple smallities that bring some micro-mini to your day.

+ About Us +
+
+ +
+
+
+ + + +
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000000..a2b53fd227 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,130 @@ + + + + + Itsy + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + +
+ +
+

<%= link_to "Itsy", root_path %>

+ <%#

Itsy Bitsy Things For A Big Crowded World

%> + +
+ <% if flash[:result_text] || flash[:messages] %> +
+
+ <%= flash[:status].to_s == "failure" ? "A problem occurred: " : "" %><%= flash[:result_text] %> + <% if flash[:messages] %> +
    + <% flash[:messages].each do |name, problems| %> + <% problems.each do |problem| %> +
  • <%= name %>: <%= problem %>
  • + <% end %> + <% end %> +
+ <% end %> +
+
+ <% end %> +
+ <%= yield %> +
+ +
+