forked from fixin.me/fixin.me
Add Units
This commit is contained in:
1
app/assets/images/pictograms/delete-outline.svg
Normal file
1
app/assets/images/pictograms/delete-outline.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="icon" viewBox="0 0 24 24"><path d="M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19M8,9H16V19H8V9M15.5,4L14.5,3H9.5L8.5,4H5V6H19V4H15.5Z" /></svg>
|
||||
|
After Width: | Height: | Size: 189 B |
1
app/assets/images/pictograms/plus-outline.svg
Normal file
1
app/assets/images/pictograms/plus-outline.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="icon" viewBox="0 0 24 24"><path d="M4,9H9V4H15V9H20V15H15V20H9V15H4V9M11,13V18H13V13H18V11H13V6H11V11H6V13H11Z" /></svg>
|
||||
|
After Width: | Height: | Size: 165 B |
1
app/assets/images/pictograms/weight-kilogram.svg
Normal file
1
app/assets/images/pictograms/weight-kilogram.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="icon" viewBox="0 0 24 24"><path d="M12,3A4,4 0 0,1 16,7C16,7.73 15.81,8.41 15.46,9H18C18.95,9 19.75,9.67 19.95,10.56C21.96,18.57 22,18.78 22,19A2,2 0 0,1 20,21H4A2,2 0 0,1 2,19C2,18.78 2.04,18.57 4.05,10.56C4.25,9.67 5.05,9 6,9H8.54C8.19,8.41 8,7.73 8,7A4,4 0 0,1 12,3M12,5A2,2 0 0,0 10,7A2,2 0 0,0 12,9A2,2 0 0,0 14,7A2,2 0 0,0 12,5M6,11V19H8V16.5L9,17.5V19H11V17L9,15L11,13V11H9V12.5L8,13.5V11H6M15,11C13.89,11 13,11.89 13,13V17C13,18.11 13.89,19 15,19H18V14H16V17H15V13H18V11H15Z" /></svg>
|
||||
|
After Width: | Height: | Size: 537 B |
@@ -102,13 +102,17 @@ input:read-only:hover {
|
||||
.nav-menu .left > * {
|
||||
float: left;
|
||||
}
|
||||
/* TODO: inactive tab color #d0d0d0 or #c7c7c7 */
|
||||
.nav-menu .tab {
|
||||
border: none;
|
||||
border-bottom: solid 0.2rem #a0a0a0;
|
||||
border-radius: 0;
|
||||
font-size: 0.9rem;
|
||||
margin: 0 0.8rem;
|
||||
padding: 0.5rem 0.8rem;
|
||||
}
|
||||
.nav-menu .tab:hover {
|
||||
border-bottom: solid 0.2rem #009ade;
|
||||
}
|
||||
.nav-menu .tab.active {
|
||||
border-bottom: solid 0.2rem;
|
||||
color: #009ade;
|
||||
@@ -276,20 +280,23 @@ table.items tbody tr:hover {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
table.items th,
|
||||
table.items td:not(:first-child):not(.actions) {
|
||||
table.items td {
|
||||
padding: 0 0.8rem;
|
||||
text-align: center;
|
||||
}
|
||||
table.items td {
|
||||
border-top: 1px solid #dddddd;
|
||||
}
|
||||
table.items td:first-child {
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
table.items a {
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
line-height: 2.5rem;
|
||||
line-height: 2.2rem;
|
||||
padding: 0 0.8rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -318,11 +325,15 @@ table.items svg {
|
||||
vertical-align: middle;
|
||||
width: 1.2rem;
|
||||
}
|
||||
table.items td.number {
|
||||
text-align: right;
|
||||
}
|
||||
table.items td.actions {
|
||||
padding-left: 0.8rem;
|
||||
padding: 0 0 0 0.8rem;
|
||||
text-align: right;
|
||||
}
|
||||
table.items button {
|
||||
font-weight: normal;
|
||||
margin-right: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
@@ -335,3 +346,8 @@ table.items select:focus-within,
|
||||
table.items select:focus-visible {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.contextual {
|
||||
float: right;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
57
app/controllers/units_controller.rb
Normal file
57
app/controllers/units_controller.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
class UnitsController < ApplicationController
|
||||
before_action :find_unit, only: [:edit, :update, :destroy]
|
||||
|
||||
before_action except: :index do
|
||||
raise AccessForbidden unless current_user.at_least(:active)
|
||||
end
|
||||
before_action only: [:edit, :update, :destroy] do
|
||||
raise ArgumentError unless current_user == @unit.user
|
||||
end
|
||||
|
||||
def index
|
||||
@units = current_user.units
|
||||
end
|
||||
|
||||
def new
|
||||
@unit = current_user.units.new
|
||||
end
|
||||
|
||||
def create
|
||||
@unit = current_user.units.new(unit_params)
|
||||
if @unit.save
|
||||
flash[:notice] = t(".success")
|
||||
redirect_to units_url
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
if @unit.update(unit_params)
|
||||
flash[:notice] = t(".success")
|
||||
redirect_to units_url
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @unit.destroy
|
||||
flash[:notice] = t(".success")
|
||||
end
|
||||
redirect_to units_url
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unit_params
|
||||
params.require(:unit).permit(:symbol, :name, :base_id, :multiplier)
|
||||
end
|
||||
|
||||
def find_unit
|
||||
@unit = Unit.find(params[:id])
|
||||
end
|
||||
end
|
||||
@@ -49,5 +49,4 @@ class UsersController < ApplicationController
|
||||
def find_user
|
||||
@user = User.find(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -69,21 +69,27 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
def navigation_menu
|
||||
#menu_items = {right: [[:users, :index],]}
|
||||
menu_items = {right: [
|
||||
[".users", "account-multiple-outline", users_path, :admin],
|
||||
[".units", "weight-kilogram", units_path, :restricted],
|
||||
]}
|
||||
|
||||
content_tag :div, class: "right" do
|
||||
if current_user.at_least(:admin)
|
||||
image_link_to t(".users"), "account-multiple-outline", users_path, class: "tab",
|
||||
current: :active
|
||||
menu_items.map do |alignment, items|
|
||||
content_tag :div, class: alignment do
|
||||
items.map do |label, image, path, status|
|
||||
if current_user.at_least(status)
|
||||
image_link_to t(label), image, path, class: "tab", current: :active
|
||||
end
|
||||
end.join.html_safe
|
||||
end
|
||||
end
|
||||
end.join.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def image_element_to(type, name, image = nil, options = nil, html_options = {})
|
||||
current = html_options.delete(:current)
|
||||
return "" if (current == :hide) && (url_for(options) == request.path)
|
||||
current = (url_for(options) == request.path) && html_options.delete(:current)
|
||||
return "" if current == :hide
|
||||
|
||||
name = svg_tag("pictograms/#{image}") + name if image
|
||||
html_options[:class] = class_names(html_options[:class], "button", active: current == :active)
|
||||
|
||||
26
app/models/unit.rb
Normal file
26
app/models/unit.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
class Unit < ApplicationRecord
|
||||
attribute :multiplier, default: 1
|
||||
|
||||
belongs_to :user, optional: true
|
||||
belongs_to :base, optional: true, class_name: "Unit"
|
||||
|
||||
validates :symbol, presence: true, uniqueness: {scope: :user_id}
|
||||
validates :multiplier, numericality: {equal_to: 1}, unless: :base
|
||||
validates :multiplier, numericality: {other_than: 1}, if: :base
|
||||
validate if: -> { base.present? } do
|
||||
errors.add(:base, :only_top_level_base_units) unless base.base.nil?
|
||||
end
|
||||
|
||||
acts_as_nested_set parent_column: :base_id, scope: :user, dependent: :destroy, order_column: :multiplier
|
||||
|
||||
scope :defaults, -> { where(user: nil) }
|
||||
|
||||
after_save if: :base do |record|
|
||||
record.move_to_ordered_child_of(record.base, :multiplier)
|
||||
end
|
||||
|
||||
before_destroy do
|
||||
# TODO: disallow destruction if any object depends on this unit
|
||||
nil
|
||||
end
|
||||
end
|
||||
@@ -11,6 +11,8 @@ class User < ApplicationRecord
|
||||
disabled: 0, # administratively disallowed to sign in
|
||||
}, default: :active
|
||||
|
||||
has_many :units, -> { order :lft }, dependent: :destroy
|
||||
|
||||
def at_least(status)
|
||||
User.statuses[self.status] >= User.statuses[status]
|
||||
end
|
||||
|
||||
26
app/views/units/_form.html.erb
Normal file
26
app/views/units/_form.html.erb
Normal file
@@ -0,0 +1,26 @@
|
||||
<div id="add-unit" <%= 'style=display:none;' if @unit.errors.empty? %>>
|
||||
<h2><%= t ".heading_new_unit" %></h2>
|
||||
|
||||
<%= labelled_form_for @unit,
|
||||
url: project_units_path(@project),
|
||||
html: {id: 'unit-add-form', name: 'unit-add-form'} do |f| %>
|
||||
|
||||
<%= render partial: 'units/form', locals: {f: f} %>
|
||||
<%= submit_tag l(:button_create) %>
|
||||
<%= link_to l(:button_cancel), "#", onclick: '$("#add-unit").hide(); return false;' %>
|
||||
<% end %>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<%= error_messages_for @unit %>
|
||||
|
||||
<div class="box tabular">
|
||||
<div class="splitcontent">
|
||||
<div class="splitcontentleft">
|
||||
<p><%= f.text_field :shortname, required: true, size: 20 %></p>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<p><%= f.text_field :name, size: 60 %></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1
app/views/units/edit.html.erb
Symbolic link
1
app/views/units/edit.html.erb
Symbolic link
@@ -0,0 +1 @@
|
||||
/var/www/app/views/units/new.html.erb
|
||||
35
app/views/units/index.html.erb
Normal file
35
app/views/units/index.html.erb
Normal file
@@ -0,0 +1,35 @@
|
||||
<div class="contextual">
|
||||
<% if current_user.at_least(:active) %>
|
||||
<%= image_link_to t(".add_unit"), "plus-outline", new_unit_path %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<table class="items" id="units">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= User.human_attribute_name(:symbol).capitalize %></th>
|
||||
<th><%= User.human_attribute_name(:name).capitalize %></th>
|
||||
<th><%= User.human_attribute_name(:multiplier).capitalize %></th>
|
||||
<% if current_user.at_least(:active) %>
|
||||
<th><%= t :actions %></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% Unit.each_with_level(@units) do |unit, level| %>
|
||||
<tr>
|
||||
<td <%= "style=padding-left:0.5rem;" if level > 0 %>>
|
||||
<%= link_to unit.symbol, edit_unit_path(unit) %>
|
||||
</td>
|
||||
<td><%= unit.name %></td>
|
||||
<td class="number"><%= unit.multiplier unless unit.multiplier == 1 %></td>
|
||||
<% if current_user.at_least(:active) %>
|
||||
<td class="actions">
|
||||
<%= image_button_to t(".delete_unit"), "delete-outline", unit_path(unit),
|
||||
method: :delete %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
22
app/views/units/new.html.erb
Normal file
22
app/views/units/new.html.erb
Normal file
@@ -0,0 +1,22 @@
|
||||
<% content_for :navigation, flush: true do %>
|
||||
<div class="left">
|
||||
<%= image_link_to t(:back), "arrow-left-bold-outline",
|
||||
request.referer.present? ? :back : units_url %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= tabular_form_for @unit do |f| %>
|
||||
<%= f.text_field :symbol, required: true, size: 10, autofocus: true, autocomplete: "off" %>
|
||||
<%= f.text_field :name, size: 25, autocomplete: "off" %>
|
||||
|
||||
<% if current_user.units.roots.count %>
|
||||
<%= f.select :base_id,
|
||||
current_user.units.roots.collect { |u| ["#{u.symbol}#{' - ' + u.name if u.name}", u.id] },
|
||||
{include_blank: t(".none")},
|
||||
onchange: 'this.form.unit_multiplier.disabled = (this.value == "");' %>
|
||||
<%= f.number_field :multiplier, step: "any", disabled: @unit.base.nil?, size: 10,
|
||||
autocomplete: "off" %>
|
||||
<% end %>
|
||||
|
||||
<%= f.submit @unit.persisted? ? t(:update) : t(:add) %>
|
||||
<% end %>
|
||||
Reference in New Issue
Block a user