Add Units

This commit is contained in:
2023-07-06 18:34:16 +02:00
parent 6f415dfb62
commit a4745c9cb8
21 changed files with 285 additions and 15 deletions

View 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

View 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

View 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

View File

@@ -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;
}

View 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

View File

@@ -49,5 +49,4 @@ class UsersController < ApplicationController
def find_user
@user = User.find(params[:id])
end
end

View File

@@ -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
View 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

View File

@@ -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

View 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>

View File

@@ -0,0 +1 @@
/var/www/app/views/units/new.html.erb

View 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>

View 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 %>