diff --git a/.gitignore b/.gitignore
index 40bcfe6e2..d32b29c45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,5 @@
/tmp
-config/database.yml
\ No newline at end of file
+config/database.yml
+public/uploads
diff --git a/Gemfile b/Gemfile
index e23a1da6b..d873a2f94 100644
--- a/Gemfile
+++ b/Gemfile
@@ -36,6 +36,14 @@ gem "devise"
gem "bootstrap-sass"
+gem "simple_form"
+
+gem "carrierwave"
+
+gem "mini_magick"
+
+gem "font-awesome-rails"
+
gem "awesome_rails_console" # 讓你的 rails console 變整齊漂亮的 gem
group :development, :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index 364e03c03..7c45ea8ca 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -58,6 +58,11 @@ GEM
builder (3.2.2)
byebug (5.0.0)
columnize (= 0.9.0)
+ 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.1.0)
coffee-script (>= 2.2.0)
@@ -77,6 +82,8 @@ GEM
warden (~> 1.2.3)
erubis (2.7.0)
execjs (2.5.2)
+ font-awesome-rails (4.4.0.0)
+ railties (>= 3.2, < 5.0)
globalid (0.3.5)
activesupport (>= 4.1.0)
hirb (0.7.3)
@@ -98,6 +105,7 @@ GEM
mime-types (>= 1.16, < 3)
method_source (0.8.2)
mime-types (2.6.1)
+ mini_magick (4.3.3)
mini_portile (0.6.2)
minitest (5.7.0)
multi_json (1.11.2)
@@ -158,6 +166,9 @@ GEM
sdoc (0.4.1)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
+ simple_form (3.2.0)
+ actionpack (~> 4.0)
+ activemodel (~> 4.0)
slop (3.6.0)
spring (1.3.6)
sprockets (3.2.0)
@@ -193,13 +204,17 @@ DEPENDENCIES
awesome_rails_console
bootstrap-sass
byebug
+ carrierwave
coffee-rails (~> 4.1.0)
devise
+ font-awesome-rails
jbuilder (~> 2.0)
jquery-rails
+ mini_magick
rails (= 4.2.2)
sass-rails (~> 5.0)
sdoc (~> 0.4.0)
+ simple_form
spring
sqlite3
turbolinks
diff --git a/app/assets/javascripts/admin/users.coffee b/app/assets/javascripts/admin/users.coffee
new file mode 100644
index 000000000..24f83d18b
--- /dev/null
+++ b/app/assets/javascripts/admin/users.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 e07c5a830..11374a391 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 bootstrap/dropdown
+//= require bootstrap/alert
//= require_tree .
diff --git a/app/assets/javascripts/products.coffee b/app/assets/javascripts/products.coffee
new file mode 100644
index 000000000..24f83d18b
--- /dev/null
+++ b/app/assets/javascripts/products.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/users.scss b/app/assets/stylesheets/admin/users.scss
new file mode 100644
index 000000000..2cabf7bd7
--- /dev/null
+++ b/app/assets/stylesheets/admin/users.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the admin::users 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.scss b/app/assets/stylesheets/application.css.scss
index b848adade..923602d3a 100644
--- a/app/assets/stylesheets/application.css.scss
+++ b/app/assets/stylesheets/application.css.scss
@@ -16,3 +16,13 @@
@import "bootstrap-sprockets";
@import "bootstrap";
+@import "font-awesome";
+
+.product-price{
+ padding-top: 20px;
+ padding-bottom: 20px;
+
+ font-size: 30px;
+ font-weight: bold;
+ color: #ff007c;
+}
diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss
new file mode 100644
index 000000000..89e2e8db0
--- /dev/null
+++ b/app/assets/stylesheets/products.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the products controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/controllers/admin/products_controller.rb b/app/controllers/admin/products_controller.rb
index 3568d198b..7b736c44f 100644
--- a/app/controllers/admin/products_controller.rb
+++ b/app/controllers/admin/products_controller.rb
@@ -1,5 +1,7 @@
class Admin::ProductsController < ApplicationController
+ layout "admin"
+
before_action :authenticate_user!
before_action :admin_required
@@ -9,6 +11,17 @@ def index
def new
@product = Product.new
+ @photo = @product.build_photo
+ end
+
+ def edit
+ @product = Product.find(params[:id])
+
+ if @product.photo.present?
+ @photo = @product.photo
+ else
+ @photo = @product.build_photo
+ end
end
def create
@@ -21,9 +34,20 @@ def create
end
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)
+ params.require(:product).permit(:title, :description, :quantity, :price,
+ photo_attributes: [:image, :id])
end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
new file mode 100644
index 000000000..684dcb906
--- /dev/null
+++ b/app/controllers/admin/users_controller.rb
@@ -0,0 +1,24 @@
+class Admin::UsersController < ApplicationController
+ layout "admin"
+
+ before_action :authenticate_user!
+ before_action :admin_required
+
+ def index
+ @users = User.all
+ end
+
+ def to_admin
+ @user = User.find(params[:id])
+ @user.to_admin
+
+ redirect_to admin_users_path
+ end
+
+ def to_normal
+ @user = User.find(params[:id])
+ @user.to_normal
+
+ redirect_to admin_users_path
+ end
+end
diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb
new file mode 100644
index 000000000..0e295c258
--- /dev/null
+++ b/app/controllers/products_controller.rb
@@ -0,0 +1,9 @@
+class ProductsController < ApplicationController
+ def index
+ @products = Product.all
+ end
+
+ def show
+ @product = Product.find(params[:id])
+ end
+end
diff --git a/app/helpers/admin/users_helper.rb b/app/helpers/admin/users_helper.rb
new file mode 100644
index 000000000..e23333d37
--- /dev/null
+++ b/app/helpers/admin/users_helper.rb
@@ -0,0 +1,9 @@
+module Admin::UsersHelper
+ def render_to_normal_user_button(user, current_user)
+ if user == current_user
+ "這是你的帳號,無法變更"
+ else
+ link_to("轉為一般使用者", to_normal_admin_user_path(user), method: :post, class: "btn btn-info")
+ end
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index de6be7945..242322a84 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,2 +1,19 @@
module ApplicationHelper
+ def notice_message
+ alert_types = { notice: :success, alert: :danger }
+
+ close_button_options = { class: "close", "data-dismiss" => "alert", "aria-hidden" => true }
+ close_button = content_tag(:button, "×", close_button_options)
+
+ alerts = flash.map do |type, message|
+ alert_content = close_button + message
+
+ alert_type = alert_types[type.to_sym] || type
+ alert_class = "alert alert-#{alert_type} alert-dismissable"
+
+ content_tag(:div, alert_content, class: alert_class)
+ end
+
+ alerts.join("\n").html_safe
+ end
end
diff --git a/app/helpers/products_helper.rb b/app/helpers/products_helper.rb
new file mode 100644
index 000000000..ab5c42b32
--- /dev/null
+++ b/app/helpers/products_helper.rb
@@ -0,0 +1,2 @@
+module ProductsHelper
+end
diff --git a/app/models/photo.rb b/app/models/photo.rb
new file mode 100644
index 000000000..6250c6856
--- /dev/null
+++ b/app/models/photo.rb
@@ -0,0 +1,5 @@
+class Photo < ActiveRecord::Base
+ belongs_to :product
+
+ mount_uploader :image, ImageUploader
+end
diff --git a/app/models/product.rb b/app/models/product.rb
index 077a81979..d353e515c 100644
--- a/app/models/product.rb
+++ b/app/models/product.rb
@@ -1,2 +1,5 @@
class Product < ActiveRecord::Base
+ has_one :photo
+
+ accepts_nested_attributes_for :photo
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 2a15996af..2939357b3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -7,4 +7,12 @@ class User < ActiveRecord::Base
def admin?
is_admin
end
+
+ def to_admin
+ self.update_columns(is_admin: true)
+ end
+
+ def to_normal
+ self.update_columns(is_admin: false)
+ end
end
diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb
new file mode 100644
index 000000000..cbb614e63
--- /dev/null
+++ b/app/uploaders/image_uploader.rb
@@ -0,0 +1,59 @@
+# 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 000000000..663debd58
--- /dev/null
+++ b/app/views/admin/products/edit.html.erb
@@ -0,0 +1,32 @@
+<%= simple_form_for [:admin, @product] do |f| %>
+
+
+ <%= f.input :title %>
+
+
+
+ <%= f.input :description %>
+
+
+
+ <%= f.input :quantity %>
+
+
+
+ <%= f.input :price %>
+
+
+ <% if @photo.image.present? %>
+ 目前商品圖
+ <%= image_tag(@photo.image.thumb.url) %>
+ <% end %>
+
+
+ <%= f.simple_fields_for :photo do |c| %>
+ <%= c.input :image , as: :file %>
+ <% end %>
+
+
+ <%= f.submit "Submit", data: { disable_with: "Submitting..." } %>
+
+<% end %>
diff --git a/app/views/admin/products/index.html.erb b/app/views/admin/products/index.html.erb
index 1cc1d7c87..5cacbf0aa 100644
--- a/app/views/admin/products/index.html.erb
+++ b/app/views/admin/products/index.html.erb
@@ -1,6 +1,42 @@
-
- <% @products.each do |product| %>
-
- - <%= link_to(product.title, admin_product_path(product)) %>
- <% end %>
-
+ Product List
+
+ <%= link_to("新增產品", new_admin_product_path, class: "btn btn-primary btn-sm") %>
+
+
+
+
+ # |
+ Product Pic |
+ Name |
+ Price |
+ Options |
+
+
+
+ <% @products.each do |product| %>
+
+
+ <%= product.id %>
+ |
+
+ <%= link_to product_path(product) do %>
+ <% if product.photo.present? %>
+ <%= image_tag(product.photo.image.thumb.url, class: "thumbnail") %>
+ <% else %>
+ <%= image_tag("http://placehold.it/200x200&text=No Pic", class: "thumbnail") %>
+ <% end %>
+ <% end %>
+ |
+
+ <%= product.title %>
+ |
+
+ <%= product.price %>
+ |
+
+ <%= link_to("Edit", edit_admin_product_path(product)) %>
+ |
+
+ <% end %>
+
+
diff --git a/app/views/admin/products/new.html.erb b/app/views/admin/products/new.html.erb
index 421b15db4..b4fdf52ab 100644
--- a/app/views/admin/products/new.html.erb
+++ b/app/views/admin/products/new.html.erb
@@ -1,23 +1,25 @@
-<%= form_for [:admin, @product] do |f| %>
+<%= simple_form_for [:admin, @product] do |f| %>
- <%= f.label("標題") %>
- <%= f.text_field :title %>
+ <%= f.input :title %>
- <%= f.label("敘述") %>
- <%= f.text_area :description %>
+ <%= f.input :description %>
- <%= f.label("數量") %>
- <%= f.text_field :quantity %>
+ <%= f.input :quantity %>
- <%= f.label("價錢") %>
- <%= f.text_field :price %>
+ <%= f.input :price %>
+
+
+
+ <%= f.simple_fields_for :photo do |c| %>
+ <%= c.input :image , as: :file %>
+ <% end %>
<%= f.submit "Submit", data: { disable_with: "Submitting..." } %>
diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb
new file mode 100644
index 000000000..2d7e216db
--- /dev/null
+++ b/app/views/admin/users/index.html.erb
@@ -0,0 +1,38 @@
+使用者列表
+
+
+
+
+ # |
+ Email |
+ 權限 |
+ 功能 |
+
+
+
+ <% @users.each do |user| %>
+
+
+ <%= user.id %>
+ |
+
+ <%= user.email %>
+ |
+
+ <% if user.admin? %>
+ 管理者
+ <% else %>
+ 一般使用者
+ <% end %>
+ |
+
+ <% if user.admin? %>
+ <%= render_to_normal_user_button(user, current_user) %>
+ <% else %>
+ <%= link_to("轉為管理者", to_admin_admin_user_path(user), method: :post, class: "btn btn-primary") %>
+ <% end %>
+ |
+
+ <% end %>
+
+
diff --git a/app/views/common/_navbar.html.erb b/app/views/common/_navbar.html.erb
new file mode 100644
index 000000000..9c3e7e6c1
--- /dev/null
+++ b/app/views/common/_navbar.html.erb
@@ -0,0 +1,48 @@
+
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
new file mode 100644
index 000000000..20a9adeec
--- /dev/null
+++ b/app/views/layouts/admin.html.erb
@@ -0,0 +1,26 @@
+
+
+
+ Artstore 後台
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
+ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
+ <%= csrf_meta_tags %>
+
+
+
+ <%= render "common/navbar" %>
+
+
+
+ - <%= link_to("Products", admin_products_path) %>
+ - <%= link_to("Users", admin_users_path) %>
+
+
+
+ <%= notice_message %>
+ <%= yield %>
+
+
+
+
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index ac224341a..6e0218b4e 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -8,13 +8,11 @@
- <% if !current_user %>
- <%= link_to("登入", new_user_session_path) %>
- <% else %>
- <%= link_to("登出", destroy_user_session_path, method: :delete) %>
- <% end %>
-
-<%= yield %>
+
+ <%= render "common/navbar" %>
+ <%= notice_message %>
+ <%= yield %>
+