diff --git a/Gemfile b/Gemfile
index fba3b4e..5a35b50 100644
--- a/Gemfile
+++ b/Gemfile
@@ -38,3 +38,15 @@ gem 'spring', group: :development
# Use debugger
# gem 'debugger', group: [:development, :test]
+gem 'devise' # login/logout
+gem 'bootstrap-sass' # make beautiful front end
+gem 'simple_form'
+
+# upload image
+gem 'carrierwave'
+
+# belongs to carrierwave, it has to be installed with carrierwave.
+# used to resize the image when uploading.
+gem 'mini_magick'
+
+gem 'better_errors', group: :development
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index 53538d9..96099ef 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -28,7 +28,19 @@ GEM
thread_safe (~> 0.1)
tzinfo (~> 1.1)
arel (5.0.1.20140414130214)
+ bcrypt (3.1.7)
+ better_errors (1.1.0)
+ coderay (>= 1.0.0)
+ erubis (>= 2.6.6)
+ bootstrap-sass (3.2.0.0)
+ sass (~> 3.2)
builder (3.2.2)
+ carrierwave (0.10.0)
+ activemodel (>= 3.2.0)
+ activesupport (>= 3.2.0)
+ json (>= 1.7)
+ mime-types (>= 1.16)
+ coderay (1.1.0)
coffee-rails (4.0.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0)
@@ -36,6 +48,12 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.7.0)
+ devise (3.2.4)
+ bcrypt (~> 3.0)
+ orm_adapter (~> 0.1)
+ railties (>= 3.2.6, < 5)
+ thread_safe (~> 0.1)
+ warden (~> 1.2.3)
erubis (2.7.0)
execjs (2.2.0)
hike (1.2.3)
@@ -51,8 +69,11 @@ GEM
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.25.1)
+ mini_magick (3.7.0)
+ subexec (~> 0.2.1)
minitest (5.3.4)
multi_json (1.10.1)
+ orm_adapter (0.5.0)
polyglot (0.3.5)
rack (1.5.2)
rack-test (0.6.2)
@@ -84,6 +105,9 @@ GEM
sdoc (0.4.0)
json (~> 1.8)
rdoc (~> 4.0, < 5.0)
+ simple_form (3.0.2)
+ actionpack (~> 4.0)
+ activemodel (~> 4.0)
spring (1.1.3)
sprockets (2.11.0)
hike (~> 1.2)
@@ -95,6 +119,7 @@ GEM
activesupport (>= 3.0)
sprockets (~> 2.8)
sqlite3 (1.3.9)
+ subexec (0.2.3)
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
@@ -108,17 +133,25 @@ GEM
uglifier (2.5.0)
execjs (>= 0.3.0)
json (>= 1.8.0)
+ warden (1.2.3)
+ rack (>= 1.0)
PLATFORMS
ruby
DEPENDENCIES
+ better_errors
+ bootstrap-sass
+ carrierwave
coffee-rails (~> 4.0.0)
+ devise
jbuilder (~> 2.0)
jquery-rails
+ mini_magick
rails (= 4.1.0)
sass-rails (~> 4.0.3)
sdoc (~> 0.4.0)
+ simple_form
spring
sqlite3
turbolinks
diff --git a/app/assets/javascripts/admin/products.js.coffee b/app/assets/javascripts/admin/products.js.coffee
new file mode 100644
index 0000000..24f83d1
--- /dev/null
+++ b/app/assets/javascripts/admin/products.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index d6925fa..0506b7c 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -13,4 +13,6 @@
//= require jquery
//= require jquery_ujs
//= require turbolinks
-//= require_tree .
+//= require bootstrap/dropdown
+//= require bootstrap/alert
+//= require_tree .
\ No newline at end of file
diff --git a/app/assets/javascripts/products.js.coffee b/app/assets/javascripts/products.js.coffee
new file mode 100644
index 0000000..24f83d1
--- /dev/null
+++ b/app/assets/javascripts/products.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/stylesheets/admin/products.css.scss b/app/assets/stylesheets/admin/products.css.scss
new file mode 100644
index 0000000..da8969d
--- /dev/null
+++ b/app/assets/stylesheets/admin/products.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the admin::products controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index a443db3..b40c3d4 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -12,4 +12,6 @@
*
*= require_tree .
*= require_self
+ *= require bootstrap
+ *= require products
*/
diff --git a/app/assets/stylesheets/products.css.scss b/app/assets/stylesheets/products.css.scss
new file mode 100644
index 0000000..9a04233
--- /dev/null
+++ b/app/assets/stylesheets/products.css.scss
@@ -0,0 +1,12 @@
+// 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/
+
+.product-price{
+ padding-top: 20px;
+ padding-bottom: 20px;
+
+ font-size: 30px;
+ font-weight: bold;
+ color: #ff007c;
+}
\ No newline at end of file
diff --git a/app/controllers/admin/products_controller.rb b/app/controllers/admin/products_controller.rb
new file mode 100644
index 0000000..392dd95
--- /dev/null
+++ b/app/controllers/admin/products_controller.rb
@@ -0,0 +1,56 @@
+class Admin::ProductsController < ApplicationController
+
+ before_action :authenticate_user!
+ before_action :admin_required
+
+ def index
+ @products = Product.all
+ end
+
+
+ def new
+ @product = Product.new
+ @photo = @product.photos.new
+ end
+
+
+ def show
+ @product = Product.find(params[:id])
+ end
+
+
+ def create
+ @product = Product.new(product_params)
+
+ if @product.save
+ redirect_to admin_products_path
+ else
+ render :new
+ end
+ end
+
+
+ def edit
+ @product = Product.find(params[:id])
+ end
+
+
+ def update
+ @product = Product.find(params[:id])
+
+ if @product.update(product_params)
+ redirect_to admin_products_path
+ else
+ render :edit
+ end
+ end
+
+
+ private
+
+
+ def product_params
+ params.require(:product).permit(:title, :description, :quantity, :price, :photos_attributes => [:image] )
+ end
+
+end
\ No newline at end of file
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d83690e..b2ce5e0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -2,4 +2,11 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
+
+ def admin_required
+ if !current_user.admin?
+ redirect_to root_path, alert: 'you are not admin!!'
+ end
+ end
+
end
diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb
new file mode 100644
index 0000000..99517fa
--- /dev/null
+++ b/app/controllers/products_controller.rb
@@ -0,0 +1,11 @@
+class ProductsController < ApplicationController
+
+ def index
+ @products = Product.order("id DESC")
+ end
+
+ def show
+ @product = Product.find(params[:id])
+ end
+
+end
diff --git a/app/helpers/admin/products_helper.rb b/app/helpers/admin/products_helper.rb
new file mode 100644
index 0000000..977a242
--- /dev/null
+++ b/app/helpers/admin/products_helper.rb
@@ -0,0 +1,2 @@
+module Admin::ProductsHelper
+end
diff --git a/app/helpers/products_helper.rb b/app/helpers/products_helper.rb
new file mode 100644
index 0000000..0bb2b90
--- /dev/null
+++ b/app/helpers/products_helper.rb
@@ -0,0 +1,37 @@
+module ProductsHelper
+
+ def render_product_photo(photo, size = "thumb")
+ if photo.present?
+ image_url = photo.image.send(size).url
+ else
+
+ case size
+ when :medium
+ volume = "300x300"
+ else
+ volume = "200x200"
+ end
+
+ image_url = "http://placehold.it/#{volume}&text=No Pic"
+ end
+
+ image_tag(image_url, :class => "thumbnail")
+ end
+
+ def render_product_name(product)
+ product.title
+ end
+
+ def render_product_desc(product)
+ simple_format(product.description)
+ end
+
+ def render_product_quantity(product)
+ product.quantity
+ end
+
+ def render_product_price(product)
+ product.price
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/photo.rb b/app/models/photo.rb
new file mode 100644
index 0000000..0f734e9
--- /dev/null
+++ b/app/models/photo.rb
@@ -0,0 +1,7 @@
+class Photo < ActiveRecord::Base
+
+ belongs_to :product
+
+ mount_uploader :image, ImageUploader
+
+end
diff --git a/app/models/product.rb b/app/models/product.rb
new file mode 100644
index 0000000..e055102
--- /dev/null
+++ b/app/models/product.rb
@@ -0,0 +1,13 @@
+class Product < ActiveRecord::Base
+
+ has_many :photos
+ accepts_nested_attributes_for :photos
+
+ validates :title, :presence => true
+ validates :quantity, :presence => true
+
+ def default_photo
+ photos.first
+ end
+
+end
diff --git a/app/models/user.rb b/app/models/user.rb
new file mode 100644
index 0000000..9df3cc3
--- /dev/null
+++ b/app/models/user.rb
@@ -0,0 +1,11 @@
+class User < ActiveRecord::Base
+ # Include default devise modules. Others available are:
+ # :confirmable, :lockable, :timeoutable and :omniauthable
+ devise :database_authenticatable, :registerable,
+ :recoverable, :rememberable, :trackable, :validatable
+
+ def admin?
+ is_admin
+ end
+
+end
diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb
new file mode 100644
index 0000000..7e020fe
--- /dev/null
+++ b/app/uploaders/image_uploader.rb
@@ -0,0 +1,61 @@
+# encoding: utf-8
+
+class ImageUploader < CarrierWave::Uploader::Base
+
+ # Include RMagick or MiniMagick support:
+ # include CarrierWave::RMagick
+ include CarrierWave::MiniMagick
+
+ # Choose what kind of storage to use for this uploader:
+ storage :file
+ # storage :fog
+
+ # Override the directory where uploaded files will be stored.
+ # This is a sensible default for uploaders that are meant to be mounted:
+ def store_dir
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
+ end
+
+ process :resize_to_fit => [800, 800]
+
+ version :thumb do
+ process :resize_to_fill => [200,200]
+ end
+
+ version :medium do
+ process :resize_to_fill => [400,400]
+ end
+
+ # Provide a default URL as a default if there hasn't been a file uploaded:
+ # def default_url
+ # # For Rails 3.1+ asset pipeline compatibility:
+ # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
+ #
+ # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
+ # end
+
+ # Process files as they are uploaded:
+ # process :scale => [200, 300]
+ #
+ # def scale(width, height)
+ # # do something
+ # end
+
+ # Create different versions of your uploaded files:
+ # version :thumb do
+ # process :resize_to_fit => [50, 50]
+ # end
+
+ # Add a white list of extensions which are allowed to be uploaded.
+ # For images you might use something like this:
+ # def extension_white_list
+ # %w(jpg jpeg gif png)
+ # end
+
+ # Override the filename of the uploaded files:
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
+ # def filename
+ # "something.jpg" if original_filename
+ # end
+
+end
diff --git a/app/views/admin/products/edit.html.erb b/app/views/admin/products/edit.html.erb
new file mode 100644
index 0000000..5ad592e
--- /dev/null
+++ b/app/views/admin/products/edit.html.erb
@@ -0,0 +1,12 @@
+<%= simple_form_for [:admin, @product] do |f| %>
+
+ <%= f.input :title %>
+ <%= f.input :description %>
+ <%= f.input :quantity %>
+ <%= f.input :price %>
+
+ <%= f.submit "Submit", :disable_with => 'Submiting...' %>
+
+<% end %>
+
+<%= link_to 'Cancel', admin_products_path %>
\ No newline at end of file
diff --git a/app/views/admin/products/index.html.erb b/app/views/admin/products/index.html.erb
new file mode 100644
index 0000000..1624da5
--- /dev/null
+++ b/app/views/admin/products/index.html.erb
@@ -0,0 +1,32 @@
+
| # | +商品名稱 | +商品售價 | +庫存數量 | +功能選項 | +
|---|---|---|---|---|
| <%= product.id %> | +<%= render_product_photo(product.default_photo) %> + <%= link_to(product.title, admin_product_path(product)) %> + | +<%= product.price %> | +<%= product.quantity %> | +<%= link_to("Edit", edit_admin_product_path(product)) %> | +
+ Title: + <%= @product.title %> +
+ ++ Description: + <%= @product.description %> +
+ ++ Quantity: + <%= @product.quantity %> +
+ ++ Price: + <%= @product.price %> +
+ +<%= link_to 'Edit', edit_admin_product_path(params[:id]) %> +